BZOJ - 2588 Spoj 10628. Count on a tree (可持久化线段树+LCA/树链剖分)
第一种方法,dfs序上建可持久化线段树,然后询问的时候把两点之间的所有树链扒出来做差。
- #include<bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- const int N=1e5+,inf=0x3f3f3f3f;
- int hd[N],ne,n,n2,m,fa[N],son[N],siz[N],dep[N],top[N],dfn[N],rnk[N],tot,rt[N],ls[N*],rs[N*],val[N*],tot2,a[N],b[N],ql[],qr[],nl,nr;
- struct E {int v,nxt;} e[N<<];
- void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
- void dfs1(int u,int f,int d) {
- fa[u]=f,son[u]=,siz[u]=,dep[u]=d;
- for(int i=hd[u]; ~i; i=e[i].nxt) {
- int v=e[i].v;
- if(v==fa[u])continue;
- dfs1(v,u,d+),siz[u]+=siz[v];
- if(siz[v]>siz[son[u]])son[u]=v;
- }
- }
- void dfs2(int u,int tp) {
- top[u]=tp,dfn[u]=++tot,rnk[tot]=u;
- if(son[u])dfs2(son[u],tp);
- for(int i=hd[u]; ~i; i=e[i].nxt) {
- int v=e[i].v;
- if(v==fa[u]||v==son[u])continue;
- dfs2(v,v);
- }
- }
- #define mid ((l+r)>>1)
- void upd(int& u,int v,int x,int l=,int r=n2) {
- if(!u)u=++tot2;
- val[u]=val[v]+;
- if(l==r)return;
- if(x<=mid)upd(ls[u],ls[v],x,l,mid),rs[u]=rs[v];
- else upd(rs[u],rs[v],x,mid+,r),ls[u]=ls[v];
- }
- int ask(int u,int v,int k) {
- for(nl=nr=; top[u]!=top[v]; u=fa[top[u]]) {
- if(dep[top[u]]<dep[top[v]])swap(u,v);
- ql[nl++]=rt[dfn[top[u]]-],qr[nr++]=rt[dfn[u]];
- }
- if(dep[u]<dep[v])swap(u,v);
- ql[nl++]=rt[dfn[v]-],qr[nr++]=rt[dfn[u]];
- int l=,r=n2;
- while(l<r) {
- int cnt=;
- for(int i=; i<nr; ++i)cnt+=val[ls[qr[i]]];
- for(int i=; i<nl; ++i)cnt-=val[ls[ql[i]]];
- if(k<=cnt) {
- for(int i=; i<nr; ++i)qr[i]=ls[qr[i]];
- for(int i=; i<nl; ++i)ql[i]=ls[ql[i]];
- r=mid;
- } else {
- k-=cnt;
- for(int i=; i<nr; ++i)qr[i]=rs[qr[i]];
- for(int i=; i<nl; ++i)ql[i]=rs[ql[i]];
- l=mid+;
- }
- }
- return l;
- }
- int main() {
- memset(hd,-,sizeof hd),ne=;
- scanf("%d%d",&n,&m);
- for(int i=; i<=n; ++i)scanf("%d",&a[i]);
- for(int i=; i<=n; ++i)b[i-]=a[i];
- sort(b,b+n),n2=unique(b,b+n)-b;
- for(int i=; i<=n; ++i)a[i]=(lower_bound(b,b+n2,a[i])-b)+;
- for(int i=; i<n; ++i) {
- int u,v;
- scanf("%d%d",&u,&v);
- addedge(u,v),addedge(v,u);
- }
- tot=,dfs1(,,),dfs2(,);
- memset(rt,,sizeof rt),tot2=;
- for(int i=; i<=n; ++i)upd(rt[i],rt[i-],a[rnk[i]],,n2);
- for(int last=; m--;) {
- int u,v,k;
- scanf("%d%d%d",&u,&v,&k),u^=last;
- int ans=b[ask(u,v,k)-];
- printf("%d\n",ans),last=ans;
- }
- return ;
- }
仔细一想这样似乎麻烦了点。因为没有修改操作,我们可以直接用子结点继承父节点的方式来建线段树,然后查询的时候,用u,v的线段树减去lca的线段树再减去lca父节点的线段树即可。
- #include<bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- const int N=1e5+,inf=0x3f3f3f3f;
- int hd[N],ne,n,n2,m,fa[N],son[N],siz[N],dep[N],top[N],dfn[N],rnk[N],tot,rt[N],ls[N*],rs[N*],val[N*],tot2,a[N],b[N],ql[],qr[],nl,nr;
- struct E {int v,nxt;} e[N<<];
- void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
- void dfs1(int u,int f,int d) {
- fa[u]=f,son[u]=,siz[u]=,dep[u]=d;
- for(int i=hd[u]; ~i; i=e[i].nxt) {
- int v=e[i].v;
- if(v==fa[u])continue;
- dfs1(v,u,d+),siz[u]+=siz[v];
- if(siz[v]>siz[son[u]])son[u]=v;
- }
- }
- void dfs2(int u,int tp) {
- top[u]=tp,dfn[u]=++tot,rnk[tot]=u;
- if(son[u])dfs2(son[u],tp);
- for(int i=hd[u]; ~i; i=e[i].nxt) {
- int v=e[i].v;
- if(v==fa[u]||v==son[u])continue;
- dfs2(v,v);
- }
- }
- #define mid ((l+r)>>1)
- void upd(int& u,int v,int x,int l=,int r=n2) {
- if(!u)u=++tot2;
- val[u]=val[v]+;
- if(l==r)return;
- if(x<=mid)upd(ls[u],ls[v],x,l,mid),rs[u]=rs[v];
- else upd(rs[u],rs[v],x,mid+,r),ls[u]=ls[v];
- }
- void dfs3(int u) {
- upd(rt[u],rt[fa[u]],a[u]);
- for(int i=hd[u]; ~i; i=e[i].nxt) {
- int v=e[i].v;
- if(v==fa[u])continue;
- dfs3(v);
- }
- }
- int lca(int u,int v) {
- for(; top[u]!=top[v]; u=fa[top[u]])if(dep[top[u]]<dep[top[v]])swap(u,v);
- return dep[u]<dep[v]?u:v;
- }
- int ask(int u,int v,int w1,int w2,int k,int l=,int r=n2) {
- if(l==r)return l;
- int cnt=val[ls[u]]+val[ls[v]]-val[ls[w1]]-val[ls[w2]];
- return k<=cnt?ask(ls[u],ls[v],ls[w1],ls[w2],k,l,mid):ask(rs[u],rs[v],rs[w1],rs[w2],k-cnt,mid+,r);
- }
- int main() {
- memset(hd,-,sizeof hd),ne=;
- scanf("%d%d",&n,&m);
- for(int i=; i<=n; ++i)scanf("%d",&a[i]);
- for(int i=; i<=n; ++i)b[i-]=a[i];
- sort(b,b+n),n2=unique(b,b+n)-b;
- for(int i=; i<=n; ++i)a[i]=(lower_bound(b,b+n2,a[i])-b)+;
- for(int i=; i<n; ++i) {
- int u,v;
- scanf("%d%d",&u,&v);
- addedge(u,v),addedge(v,u);
- }
- tot=,dfs1(,,),dfs2(,);
- memset(rt,,sizeof rt),tot2=;
- dfs3();
- for(int last=; m--;) {
- int u,v,k;
- scanf("%d%d%d",&u,&v,&k),u^=last;
- int w=lca(u,v);
- int ans=b[ask(rt[u],rt[v],rt[w],rt[fa[w]],k)-];
- printf("%d\n",ans),last=ans;
- }
- return ;
- }
然后我又测试了倍增和RMQ求LCA的方法,发现居然还不如dfs序+可持久化线段树的方法快~~毕竟倍增和RMQ预处理的时间和空间复杂度都是$O(nlogn)$,而树剖只需要$O(n)$,而且查询速度也比较快。
倍增:
- #include<bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- const int N=1e5+,inf=0x3f3f3f3f;
- int hd[N],ne,n,n2,m,fa[N][],dep[N],rt[N],ls[N*],rs[N*],val[N*],tot2,a[N],b[N];
- struct E {int v,nxt;} e[N<<];
- void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
- #define mid ((l+r)>>1)
- void upd(int& u,int v,int x,int l=,int r=n2) {
- if(!u)u=++tot2;
- val[u]=val[v]+;
- if(l==r)return;
- if(x<=mid)upd(ls[u],ls[v],x,l,mid),rs[u]=rs[v];
- else upd(rs[u],rs[v],x,mid+,r),ls[u]=ls[v];
- }
- void dfs(int u,int f,int d) {
- fa[u][]=f,dep[u]=d,upd(rt[u],rt[fa[u][]],a[u]);
- for(int i=; i<; ++i)fa[u][i]=fa[fa[u][i-]][i-];
- for(int i=hd[u]; ~i; i=e[i].nxt) {
- int v=e[i].v;
- if(v==fa[u][])continue;
- dfs(v,u,d+);
- }
- }
- int lca(int u,int v) {
- if(dep[u]<dep[v])swap(u,v);
- for(int i=; dep[u]!=dep[v]; --i)if(dep[fa[u][i]]>=dep[v])u=fa[u][i];
- if(u==v)return u;
- for(int i=; i>=; --i)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
- return fa[u][];
- }
- int ask(int u,int v,int w1,int w2,int k,int l=,int r=n2) {
- if(l==r)return l;
- int cnt=val[ls[u]]+val[ls[v]]-val[ls[w1]]-val[ls[w2]];
- return k<=cnt?ask(ls[u],ls[v],ls[w1],ls[w2],k,l,mid):ask(rs[u],rs[v],rs[w1],rs[w2],k-cnt,mid+,r);
- }
- int main() {
- memset(hd,-,sizeof hd),ne=;
- scanf("%d%d",&n,&m);
- for(int i=; i<=n; ++i)scanf("%d",&a[i]);
- for(int i=; i<=n; ++i)b[i-]=a[i];
- sort(b,b+n),n2=unique(b,b+n)-b;
- for(int i=; i<=n; ++i)a[i]=(lower_bound(b,b+n2,a[i])-b)+;
- for(int i=; i<n; ++i) {
- int u,v;
- scanf("%d%d",&u,&v);
- addedge(u,v),addedge(v,u);
- }
- memset(rt,,sizeof rt),tot2=;
- dfs(,,);
- for(int last=; m--;) {
- int u,v,k;
- scanf("%d%d%d",&u,&v,&k),u^=last;
- int w=lca(u,v);
- int ans=b[ask(rt[u],rt[v],rt[w],rt[fa[w][]],k)-];
- printf("%d\n",ans),last=ans;
- }
- return ;
- }
RMQ:
- #include<bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- const int N=1e5+,inf=0x3f3f3f3f;
- int hd[N],ne,n,n2,m,fa[N],dep[N],pos[N],ST[N<<][],Log[N<<],tot,rt[N],ls[N*],rs[N*],val[N*],tot2,a[N],b[N];
- struct E {int v,nxt;} e[N<<];
- void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
- #define mid ((l+r)>>1)
- void upd(int& u,int v,int x,int l=,int r=n2) {
- if(!u)u=++tot2;
- val[u]=val[v]+;
- if(l==r)return;
- if(x<=mid)upd(ls[u],ls[v],x,l,mid),rs[u]=rs[v];
- else upd(rs[u],rs[v],x,mid+,r),ls[u]=ls[v];
- }
- void dfs(int u,int f,int d) {
- fa[u]=f,dep[u]=d,ST[++tot][]=u,pos[u]=tot,upd(rt[u],rt[fa[u]],a[u]);
- for(int i=hd[u]; ~i; i=e[i].nxt) {
- int v=e[i].v;
- if(v==fa[u])continue;
- dfs(v,u,d+),ST[++tot][]=u;
- }
- }
- bool cmp(int a,int b) {return dep[a]<dep[b];}
- void initST() {
- for(int j=; j<; ++j)
- for(int i=; i+(<<j)-<=tot; ++i)
- ST[i][j]=min(ST[i][j-],ST[i+(<<(j-))][j-],cmp);
- }
- int lca(int u,int v) {
- int l=pos[u],r=pos[v];
- if(l>r)swap(l,r);
- int i=Log[r-l+];
- return min(ST[l][i],ST[r-(<<i)+][i],cmp);
- }
- int ask(int u,int v,int w1,int w2,int k,int l=,int r=n2) {
- if(l==r)return l;
- int cnt=val[ls[u]]+val[ls[v]]-val[ls[w1]]-val[ls[w2]];
- return k<=cnt?ask(ls[u],ls[v],ls[w1],ls[w2],k,l,mid):ask(rs[u],rs[v],rs[w1],rs[w2],k-cnt,mid+,r);
- }
- int main() {
- for(int i=; i<(N<<); ++i)Log[i]=log2(i+0.5);
- memset(hd,-,sizeof hd),ne=;
- scanf("%d%d",&n,&m);
- for(int i=; i<=n; ++i)scanf("%d",&a[i]);
- for(int i=; i<=n; ++i)b[i-]=a[i];
- sort(b,b+n),n2=unique(b,b+n)-b;
- for(int i=; i<=n; ++i)a[i]=(lower_bound(b,b+n2,a[i])-b)+;
- for(int i=; i<n; ++i) {
- int u,v;
- scanf("%d%d",&u,&v);
- addedge(u,v),addedge(v,u);
- }
- memset(rt,,sizeof rt),tot2=;
- dfs(,,),initST();
- for(int last=; m--;) {
- int u,v,k;
- scanf("%d%d%d",&u,&v,&k),u^=last;
- int w=lca(u,v);
- int ans=b[ask(rt[u],rt[v],rt[w],rt[fa[w]],k)-];
- printf("%d\n",ans),last=ans;
- }
- return ;
- }
BZOJ - 2588 Spoj 10628. Count on a tree (可持久化线段树+LCA/树链剖分)的更多相关文章
- BZOJ 2588: Spoj 10628. Count on a tree [树上主席树]
2588: Spoj 10628. Count on a tree Time Limit: 12 Sec Memory Limit: 128 MBSubmit: 5217 Solved: 1233 ...
- BZOJ 2588: Spoj 10628. Count on a tree 树上跑主席树
2588: Spoj 10628. Count on a tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/J ...
- Bzoj 2588: Spoj 10628. Count on a tree 主席树,离散化,可持久,倍增LCA
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2588 2588: Spoj 10628. Count on a tree Time Limit ...
- BZOJ 2588: Spoj 10628. Count on a tree( LCA + 主席树 )
Orz..跑得还挺快的#10 自从会树链剖分后LCA就没写过倍增了... 这道题用可持久化线段树..点x的线段树表示ROOT到x的这条路径上的权值线段树 ----------------------- ...
- Bzoj 2588 Spoj 10628. Count on a tree(树链剖分LCA+主席树)
2588: Spoj 10628. Count on a tree Time Limit: 12 Sec Memory Limit: 128 MB Description 给定一棵N个节点的树,每个点 ...
- bzoj 2588 Spoj 10628. Count on a tree (可持久化线段树)
Spoj 10628. Count on a tree Time Limit: 12 Sec Memory Limit: 128 MBSubmit: 7669 Solved: 1894[Submi ...
- 主席树 || 可持久化线段树 || LCA || BZOJ 2588: Spoj 10628. Count on a tree || Luogu P2633 Count on a tree
题面: Count on a tree 题解: 主席树维护每个节点到根节点的权值出现次数,大体和主席树典型做法差不多,对于询问(X,Y),答案要计算ans(X)+ans(Y)-ans(LCA(X,Y) ...
- BZOJ 2588: Spoj 10628. Count on a tree 主席树+lca
分析:树上第k小,然后我想说的是主席树并不局限于线性表 详细分析请看http://www.cnblogs.com/rausen/p/4006116.html,讲的很好, 然后因为这个熟悉了主席树,真是 ...
- ●BZOJ 2588 Spoj 10628. Count on a tree
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2588 题解: 主席树,在线,(求LCA)感觉主席树真的好厉害...在原树上建主席树.即对于原 ...
随机推荐
- spring 异步处理request
转自:http://blog.csdn.net/u012410733/article/details/52124333Spring MVC 3.2开始引入Servlet 3中的基于异步的处理reque ...
- 请求json和xml数据时的方式
当请求xml数据时,直接通过NSMutableData接收后解析, NSURL *url = [NSURL URLWithString:PATH]; _receiveData = [[NSMutabl ...
- 【LeetCode】【动态规划】Edit Distance
描述 Given two words word1 and word2, find the minimum number of operations required to convert word1 ...
- 吐槽 坑爹的MySQL安装路径选择
一般再windows下面安装MySQL我们都会选择msi安装模式,然而安装最新版的MySQL(mysql-installer-community-5.7.11.0.msi 下载地址)发现MySQL默认 ...
- tcp/ip 中的分组和分片
osi 大家应该都知道osi七层模型吧,物理层 链路层 网络层 传输层 会话层 表示层 应用层ip 属于网络层,tcp 属于传输层,你可以把每一层想像成粽子的粽叶,包裹了七层的粽子最外面的就是物理层, ...
- TypeScript手册1 - 基本类型和接口
基本类型 像基本的JavaScript一样,TypeScript支持numbers, strings, structures, boolean等基本类型,而扩展的enum等类型,为编程提供了更多帮助. ...
- ASP.NET5 MVC6 利用Middleware 创建可访问HttpContext 的业务类工厂。(代替HttpContext.Current)
我们的目标是在后台业务处理类中,能够很容易的取得用户信息或者其它HTTP请求相关的信息. 所以,首先我们需要一个存储这些信息的类: public class RequestData { public ...
- R语言学习笔记(1)
第一章:R语言介绍 一 R的使用 1 R是一种区分大小写的解释型语言.R语句由函数和赋值构成.R使用<-作为赋值符号.例如: x<-rnorm(5) 创建了一个名为x的向量对象,它包含5个 ...
- UVA 12307 Smallest Enclosing Rectangle(旋转卡壳)
题意:给你一些点,找出两个可以包含所有点的矩形,一个保证矩形面积最小,一个保证矩形周长最小,输出两个最小值 题解:首先根据所有点求一个凸包,再在这个凸包上枚举每条边,作为矩形的一条边(这样可以保证最小 ...
- 【转】Android ImageView圆形头像
Android ImageView圆形头像 图片完全解析 我们在做项目的时候会用到圆形的图片,比如用户头像,类似QQ.用户在用QQ更换头像的时候,上传的图片都是矩形的,但显示的时候确是圆形的. 原理: ...