BZOJ2588Count on a tree——LCA+主席树
题目描述
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。
输入
第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问。
输出
M行,表示每个询问的答案。最后一个询问不输出换行符
样例输入
8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
0 5 2
10 5 3
11 5 4
110 8 2
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
0 5 2
10 5 3
11 5 4
110 8 2
样例输出
2
8
9
105
7
8
9
105
7
提示
HINT:
N,M<=100000
查询一个路径上第k小,就相当于查询序列上区间第k小,可以想到用主席树维护。但这道题是在树上完成的操作,所以并不能像平常主席树一样只用r时刻减掉l-1时刻的线段树查询。对于树上的路径(以x和y之间的路径为例),可以把它分成两部分:x到lca和y到lca。这样整条路径上的信息就可以通过这两条链相加得到。所以直接用x时刻线段树+y时刻线段树-lca时刻线段树-lca的父节点时刻线段树就得到路径上状态。每个时刻线段树由它的父节点时刻线段树转移过来。
最后附上代码。
#include<map> #include<set> #include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define mid (L+R)/2 using namespace std; int ans; int tot; int cnt; int anc; int n,m,q; int x,y,z; int d[100010]; int v[100010]; int h[100010]; int l[3000010]; int r[3000010]; int to[200010]; int head[100010]; int next[200010]; int sum[3000010]; int root[100010]; int f[100010][20]; map<int,int>b; void add(int x,int y) { tot++; next[tot]=head[x]; head[x]=tot; to[tot]=y; } int lca(int x,int y) { if(d[x]<d[y]) { swap(x,y); } int dep=d[x]-d[y]; for(int i=0;i<=19;i++) { if((dep&(1<<i))!=0) { x=f[x][i]; } } if(x==y) { return x; } for(int i=19;i>=0;i--) { if(f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } } return f[x][0]; } int updata(int pre,int L,int R,int k) { int rt=++cnt; l[rt]=l[pre]; r[rt]=r[pre]; sum[rt]=sum[pre]+1; if(L==R) { return rt; } else { if(k<=mid) { l[rt]=updata(l[pre],L,mid,k); } else { r[rt]=updata(r[pre],mid+1,R,k); } } return rt; } int query(int x,int y,int anc,int fa,int L,int R,int k) { if(L==R) { return b[L]; } int num=sum[l[x]]+sum[l[y]]-sum[l[anc]]-sum[l[fa]]; if(num>=k) { return query(l[x],l[y],l[anc],l[fa],L,mid,k); } else { return query(r[x],r[y],r[anc],r[fa],mid+1,R,k-num); } } void dfs(int x,int fa) { d[x]=d[fa]+1; int k=lower_bound(h+1,h+1+m,v[x])-h; b[k]=v[x]; root[x]=updata(root[fa],1,n,k); for(int i=1;i<=19;i++) { f[x][i]=f[f[x][i-1]][i-1]; } for(int i=head[x];i;i=next[i]) { if(to[i]!=fa) { f[to[i]][0]=x; dfs(to[i],x); } } } int main() { scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) { scanf("%d",&v[i]); h[i]=v[i]; } sort(h+1,h+1+n); m=unique(h+1,h+1+n)-h-1; for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(1,0); for(int i=1;i<=q;i++) { scanf("%d%d%d",&x,&y,&z); x=x^ans; anc=lca(x,y); ans=query(root[x],root[y],root[anc],root[f[anc][0]],1,n,z); printf("%d\n",ans); } }