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)感觉主席树真的好厉害...在原树上建主席树.即对于原 ...
随机推荐
- Hadoop学习基础之三:MapReduce
现在是讨论这个问题的不错的时机,因为最近媒体上到处充斥着新的革命所谓“云计算”的信息.这种模式需要利用大量的(低端)处理器并行工作来解决计算问题.实际上,这建议利用大量的低端处理器来构建数据中心,而不 ...
- MySQL数据库(2)_MySQL数据库和数据库表操作语句
一.关于数据库操作的sql语句 -- .创建数据库(在磁盘上创建一个对应的文件夹) create database [if not exists] db_name [character set xxx ...
- Python基础(19)_异常处理
一.异常处理 错误和异常: 1.错误的种类: 1)语法错误:这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正 2)逻辑错误: 例如: res1=1/0 .es2=1+'str ...
- 前端之 JQuery
一.基本选择器 1.#id 概述: 根据给定的ID匹配一个元素. 使用任何的元字符(如 !"#$%&'()*+,./:;<=>?@[\]^`{|}~)作为名称的文本部分, ...
- matlab 读取nc
在这里做个记录,这几个是matlab用来读取.nc格式数据的函数.只是函数,参数和变量为了便于理解,取括号中的名字. fid=netcdf.open('fname','nowriter');%打开 ...
- DataTable Group By或运算 Linq Aggregate的使用
class Program { static void Main(string[] args) { DataTable dt = new DataTable(); dt.Columns.Add(&qu ...
- 【TopCoder】SRM160 DIV1总结
做了两道题之后才发现做的是DIV1,不是DIV2,DIV1的第二道题是DIV1的第三道题,果断决定第3题就不看了=.= 250分题:给定一个时间起点8:00 AM DAY 1,再给出一组时间终点,格式 ...
- mini2440移植uboot 2014.04(六)
上一篇博文:<mini2440移植uboot 2014.04(五)> 代码已经上传到github上:https://github.com/qiaoyuguo/u-boot-2014.04- ...
- INSPIRED启示录 读书笔记 - 第12章 产品探索
软件项目可以划分为两个阶段 探索产品阶段:弄清楚要开发什么产品(定义正确的产品) 在探索产品的阶段,产品经理负责分析各种创意,广泛收集用户需求,了解如何运用新技术,拿出产品原型并加以测试 从全局视角思 ...
- SQL Server 利用WITH AS递归获取层级关系数据
WITH AS短语,也叫做子查询部分(subquery factoring),在SQL Server 2005中提供了一种解决方案,这就是公用表表达式(CTE),使用CTE,可以使SQL语句的可维护性 ...