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)感觉主席树真的好厉害...在原树上建主席树.即对于原 ...
随机推荐
- Nginx反向代理+负载均衡简单实现
一.基础环境: 负 载 机:A机器: 192.168.71.223后端机器1:B机器:192.168.71.224后端机器2:C机器:192.168.71.226 需求: 1)访问A机器的808 ...
- Linux Shell基础 环境变量配置文件
source命令:使环境变量配置文件强制生效 source 命令会强制执行脚本中的全部命令,而忽略脚本文件的权限.该命令主要用于让重新配置的环境变量配置文件强制生效.source 命令格式如下: [r ...
- 【HackerRank】Maximizing XOR
给定两个整数:L 和 R ∀ L ≤ A ≤ B ≤ R, 找出 A xor B 的最大值. 输入格式 第一行包含 L 第一行包含 R 数据范围 1 ≤ L ≤ R ≤ 103 输出格式 输出最大的异 ...
- Linux环境下的图形系统和AMD R600显卡编程(2)——Framebuffer、DRM、EXA和Mesa简介
转:https://www.cnblogs.com/shoemaker/p/linux_graphics02.html 1. Framebuffer Framebuffer驱动提供基本的显示,fram ...
- Squid 安装
Squid简介 Squid是比较知名的代理软件,它不仅可以跑在linux上还可以跑在windows以及Unix上,它的技术已经非常成熟.目前使用Squid的用户也是十分广泛的.Squid与Linux下 ...
- Win32 API编程:WinMain无法重载函数或_tWinMain无法重载
#include "windows.h" #include "tchar.h" int APIENTRY _tWinMain( HINSTANCE hInsta ...
- JDK的安装配置
1.下载JDK安装包(http://www.oracle.com/technetwork/java/javase/downloads/index.html),现在Java已经更新到JDK 8了,但是很 ...
- Freemarker中大于号>的使用
在Freemarker中,比较数据的大小时候,要注意大于号(>)的使用.如果不注意,程序就会发生异常信息,如下面的例子: 1 2 3 4 <#assign x = 4> < ...
- 通用Mapper(Mybatis)
1.Mapper的简单介绍 2.Mapper的作用 通用Mapper可以通过Mybatis的拦截器原理,动态的帮我们实现单表的增删改查功能,大大降低了我们的开发成本,减少了我们的工作量. 3.Mapp ...
- java中的特殊有用类
1.MessageDigest:类似与md5加密算法应用的功能类