BZOJ2588 Count on a tree
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2588
知识点: 可持久化线段树
解题思路:
先建一棵空的权值线段树,然后按照题目给出的树以任意一点为根的\(DFS\)序来更新这棵线段树。询问\((u,v,k)\)时,其实就是查询\(T[u]\)所对应的线段树加上\(T[v]\)所对应的线段树减去\(T[u and v's LCA]\)所对应的线段树再减去\(T[fa[u and v's LCA]]\),\(LCA\)就是最近公共祖先。(从题解那里学到了一种类似二分的\(query\)的方法)
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 100000+5; 4 int inp[maxn]; //原始输入 5 int num[maxn],san[maxn]; //san[]:离散化; num[]:下标是DFS序(从1开始),表示这个数在san[]中是第几个 6 int pos[maxn]; //num[]的下标->inp[]的下标 7 int rpos[maxn]; //inp[]的下标->num[]的下标 8 int tot=0,xu=1; 9 int sum[maxn*20]; 10 int T[maxn*20],lson[maxn*20],rson[maxn*20]; 11 vector<int> G[maxn]; 12 int par[20][maxn],depth[maxn]; //LCA 13 14 void build(int l,int r,int &rt){ 15 rt=++tot; 16 sum[rt]=0; 17 if(l==r) return; 18 int m=(l+r)>>1; 19 build(l,m,lson[rt]); build(m+1,r,rson[rt]); 20 } 21 void update(int last,int pos,int l,int r,int &rt){ 22 rt=++tot; 23 sum[rt]=sum[last]+1; 24 if(l==r) return; 25 lson[rt]=lson[last]; 26 rson[rt]=rson[last]; 27 int m=(l+r)>>1; 28 if(pos<=m) update(lson[last],pos,l,m,lson[rt]); 29 else update(rson[last],pos,m+1,r,rson[rt]); 30 } 31 int lca(int u,int v){ 32 if(depth[u]>depth[v]) swap(u,v); 33 for(int k=0;k<20;k++){ 34 if((depth[v]-depth[u])>>k&1){ 35 v=par[k][v]; 36 } 37 } 38 if(u==v) return u; 39 for(int k=19;k>=0;k--){ 40 if(par[k][u]!=par[k][v]){ 41 u=par[k][u]; 42 v=par[k][v]; 43 } 44 } 45 return par[0][u]; 46 } 47 void dfs(int v,int p,int d,int cnt){ 48 num[xu]=lower_bound(san + 1, san + cnt + 1, inp[v]) - san; 49 pos[xu]=v,rpos[v]=xu; 50 xu++; 51 52 par[0][v]=p; 53 depth[v]=d; 54 for(int i=0;i<G[v].size();i++){ 55 if(G[v][i]!=p) dfs(G[v][i],v,d+1,cnt); 56 } 57 } 58 void init(int N,int cnt){ 59 dfs(1,0,1,cnt); 60 for(int k=0;k+1<20;k++){ 61 for(int v=1;v<=N;v++){ 62 if(par[k][v]<=0) par[k+1][v]=0; 63 else par[k+1][v]=par[k][par[k][v]]; 64 } 65 } 66 } 67 int que(int x,int y,int rk,int cnt){ 68 int a=x,b=y,c=lca(x,y),d=par[0][c]; 69 a=T[rpos[a]],b=T[rpos[b]],c=T[rpos[c]],d=T[rpos[d]]; 70 int l=1,r=cnt; 71 int ret=1; 72 while(l<r){ 73 int m=(l+r)>>1; 74 int tmp=sum[lson[a]]+sum[lson[b]]-sum[lson[c]]-sum[lson[d]]; 75 if(tmp>=rk) r=m,a=lson[a],b=lson[b],c=lson[c],d=lson[d]; 76 else rk-=tmp,l=m+1,a=rson[a],b=rson[b],c=rson[c],d=rson[d]; 77 } 78 return san[l]; 79 } 80 int main(){ 81 // freopen("in.txt","r",stdin); 82 int N,M; 83 int u,v,k; 84 scanf("%d%d",&N,&M); 85 for(int i=1;i<=N;i++){ 86 scanf("%d",&inp[i]); 87 san[i]=inp[i]; 88 } 89 sort(san+1,san+1+N); 90 int cnt=unique(san+1,san+1+N)-san-1; 91 build(1,cnt,T[0]); //先建一棵空树 92 for(int i=1;i<N;i++){ 93 scanf("%d%d",&u,&v); 94 G[u].push_back(v); 95 G[v].push_back(u); 96 } 97 init(N,cnt); 98 for(int i=1;i<=N;i++){ 99 int now=pos[i]; 100 int fa=par[0][now]; 101 update(T[rpos[fa]],num[i],1,cnt,T[i]); //旧版本是该点的父亲所对应的那个版本 102 } 103 int last=0; 104 while(M--){ 105 scanf("%d%d%d",&u,&v,&k); 106 u^=last; 107 last=que(u,v,k,cnt); 108 printf("%d",last); 109 if(M) printf("\n"); 110 } 111 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”