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)感觉主席树真的好厉害...在原树上建主席树.即对于原 ...
随机推荐
- Django——admin组件
Django提供了基于web的管理工具. Django自动管理工具是django.contrib的一部分.你可以在项目的settings.py中的INSTALLED_APPS看到它: # Applic ...
- python常用模块-2
一 .time模块 表示时间的三种方式: 时间戳:数字(计算机能认识的) 时间字符串:t='2012-12-12' 结构化时间:time.struct_time(tm_year=2017, tm_mo ...
- android studio的安装和卸载
安装: (待补充) 卸载: (如何彻底卸载才能达到第二次安装不受第一次安装失败的影响呢?) 1.找到安装目录,运行卸载文件.(不用清注册表,这是和卸载mysql的不同,只要把相关的文件夹,文件清楚即可 ...
- Loadrunder之脚本篇——参数化同行取值
select next row 记录选择方式 Same line as,这个选项只有当参数多余一个时才会出现,其作用是根据某一个参数的行号取同一行. 例中的做法如下: 将多个参数存放在一个参数文件中: ...
- MVC,MVP和MVVM区别
复杂的软件必须有清晰合理的架构,否则无法开发和维护. MVC(Model-View-Controller)是最常见的软件架构之一,业界有着广泛应用.它本身很容易理解,但是要讲清楚,它与衍生的 MVP ...
- 转:Windows下USB接口驱动技术(一)
- 使用git从本地上传至git码云远程仓库
从 http://git-scm.com/download 下载window版的客户端.下载好,一步一步安装即可. 使用前的基本设置 git config --global user.name & ...
- CentOS 5下freeswitch中集成使用ekho实现TTS功能三
四:在freeswitch中调用ekho 注:在测试过程中该语音包好像没用 FreeSWITCH 中文语音包测试版fssounds.zip 在/usr/local/freeswitch/sounds/ ...
- CSS实现三角形图标的原理!!!!今天总算弄懂了。
网页中经常有一种三角形的图标,鼠标点一下会弹出一个下拉菜单之类的(之前淘宝也有,不过现在改版好像没有了) 之前以为是个png图标背景,后来在bootstrap中看到有一个图标样式叫做caret的用来实 ...
- ACM训练小结-2018年6月19日
今天题目情况如下: A题:考察图论建模+判割点.B题:考察基础数据结构的运用(STL).C题:考察数学建模+运算.(三分可解)D题:考察读题+建模+数据结构的运用.E题:考察图论+贪心.F题:考察图 ...