Description
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。
Input
第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问。
Output
M行,表示每个询问的答案。
Sample Input
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
Sample Output
2
8
9
105
7
8
9
105
7
HINT
HINT:
N,M<=100000
暴力自重。。。
【题意】
求u到v路径上的第k小的数。
【思路】
主席树,dfs序,lca,二分
主席树就是可持久化线段树,即保留历史版本的线段树。
为什么要保留历史版本呢?因为我们要完成对区间的查询,对于一个区间和的询问我们可以利用前缀和的思想来解决,主席树大概也是这个思路。
我们对每一个点建一棵线段树,每一棵线段树以权值为下标,维护区间中点的数目。依次建立每一棵线段树,第i棵线段树是从第i-1棵线段树上插了个权值i而得到的。这样对于区间[l,r]的查询,我们可以通过询问T[r]和T[l-1]的相应区间而得到。
对应到这道题中,我们先离散化权值(不离散化空间还不得飞起来233),然后按照树的形态来建立每一棵线段树,每一棵线段树就是从父节点对应的线段树上拓展来的,相当于上一个版本。这样从祖先到结点路径上的线段树就是逐渐增大且只包括路径上的权值的。
建好主席树后,对于查询(u,v),二分数的大小mid,我们就可以知道“区间”内所有小于mid的数目,为T[u].lc.sum-T[lca].lc.sum+T[v].lc.sum-T[fa[lca]].lc.sum (T[i]为i对应一棵线段树,lc为左儿子,sum为对应的权值数目),如果rank比之小则缩小数的区间,使u,v,lca,fa[lca]进入左儿子,否则修改rank后进入右儿子。
【代码】
1 #include<cstdio> 2 #include<vector> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 7 using namespace std; 8 9 typedef long long ll; 10 const int N = 1e5+5; 11 const int M = 2*1e6+5; 12 const int D = 18; 13 14 int n,m,tot,sz,ind; 15 int hash[N],pos[N],num[N],v[N],root[N]; 16 int sum[M],ls[M],rs[M]; 17 vector<int> g[N]; 18 19 ll read() { 20 char c=getchar(); 21 ll f=1,x=0; 22 while(!isdigit(c)) { 23 if(c=='-')f=-1; c=getchar(); 24 } 25 while(isdigit(c)) 26 x=x*10+c-'0', c=getchar(); 27 return x*f; 28 } 29 int fa[N],top[N],son[N],siz[N],dep[N]; 30 void dfs1(int u) 31 { 32 siz[u]=1; son[u]=0; num[++ind]=u; pos[u]=ind; 33 for(int i=0;i<g[u].size();i++) { 34 int v=g[u][i]; 35 if(v!=fa[u]) { 36 fa[v]=u; 37 dep[v]=dep[u]+1; 38 dfs1(v); 39 siz[u]+=siz[v]; 40 if(siz[v]>siz[son[u]]) son[u]=v; 41 } 42 } 43 } 44 void dfs2(int u,int tp) 45 { 46 top[u]=tp; 47 if(son[u]) dfs2(son[u],tp); 48 for(int i=0;i<g[u].size();i++) { 49 int v=g[u][i]; 50 if(v!=fa[u] && v!=son[u]) dfs2(v,v); 51 } 52 } 53 int lca(int u,int v) 54 { 55 while(top[u]!=top[v]) { 56 if(dep[top[u]]<dep[top[v]]) swap(u,v); 57 u=fa[top[u]]; 58 } 59 return dep[u]>dep[v]? v:u; 60 } 61 void update(int l,int r,int x,int &y,int num) 62 { 63 y=++sz; 64 sum[y]=sum[x]+1; 65 if(l==r) return ; 66 ls[y]=ls[x] ; rs[y]=rs[x]; 67 int mid=(l+r)>>1; 68 if(num<=mid) update(l,mid,ls[x],ls[y],num); 69 else update(mid+1,r,rs[x],rs[y],num); 70 } 71 int query(int x,int y,int rank) 72 { 73 int a=x,b=y,c=lca(x,y),d=fa[c]; 74 a=root[pos[a]],b=root[pos[b]],c=root[pos[c]],d=root[pos[d]]; 75 int l=1,r=tot; 76 while(l<r) { 77 int mid=(l+r)>>1; 78 int now=sum[ls[a]]+sum[ls[b]]-sum[ls[c]]-sum[ls[d]]; 79 if(rank<=now) r=mid,a=ls[a],b=ls[b],c=ls[c],d=ls[d]; 80 else l=mid+1,rank-=now,a=rs[a],b=rs[b],c=rs[c],d=rs[d]; 81 } 82 return hash[l]; 83 } 84 85 int main() 86 { 87 //freopen("in.in","r",stdin); 88 //freopen("out.out","w",stdout); 89 n=read(),m=read(); 90 FOR(i,1,n) { 91 v[i]=read(); hash[i]=v[i]; 92 } 93 sort(hash+1,hash+n+1); 94 tot=1; 95 FOR(i,2,n) if(hash[i]!=hash[i-1]) 96 hash[++tot]=hash[i]; 97 FOR(i,1,n) 98 v[i]=lower_bound(hash+1,hash+tot+1,v[i])-hash; 99 int x,y,z; 100 FOR(i,1,n-1) { 101 x=read(),y=read(); 102 g[x].push_back(y); 103 g[y].push_back(x); 104 } 105 dfs1(1),dfs2(1,1); 106 FOR(i,1,n) { 107 int t=num[i]; 108 update(1,tot,root[pos[fa[t]]],root[i],v[t]); 109 } 110 int lastans=0; 111 FOR(i,1,m) { 112 x=read(),y=read(),z=read(); 113 x^=lastans; 114 printf("%d",lastans=query(x,y,z)); 115 if(i!=m) puts(""); 116 } 117 return 0; 118 }