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("
");
110     }
111 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”
原文地址:https://www.cnblogs.com/Blogggggg/p/8366706.html