主席树初探--BZOJ2588: Spoj 10628. Count on a tree

n<=100000的点权树,有m<=100000个询问,每次问两个点间的第k小点权,保证有解,强制在线。

主席上树啦!类似于之前的序列不带修改询问的前缀表示法,现在只要把前缀当成某点到根的信息即可。然后比如要问x点和y点,z为lca(x,y),w为z的爸爸,那么x,y,z,w四棵线段树一起跑即可。

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<algorithm>
  4 #include<stdlib.h>
  5 //#include<iostream>
  6 using namespace std;
  7 
  8 int n,m;
  9 #define maxn 100011
 10 #define maxm 2000011
 11 struct SMT
 12 {
 13     struct Node
 14     {
 15         int son[2];
 16         int cnt;
 17     }a[maxm];
 18     int size,n;
 19     void clear(int m) {n=m;size=0;a[0].cnt=0;}
 20     void build(int pre,int &rt,int L,int R,int num)
 21     {
 22         rt=++size;
 23         a[rt].cnt=a[pre].cnt+1;
 24         if (L==R) {a[rt].son[0]=a[rt].son[1]=0;return;}
 25         const int mid=(L+R)>>1;
 26         if (num<=mid) build(a[pre].son[0],a[rt].son[0],L,mid,num),a[rt].son[1]=a[pre].son[1];
 27         else build(a[pre].son[1],a[rt].son[1],mid+1,R,num),a[rt].son[0]=a[pre].son[0];
 28     }
 29     void build(int pre,int &rt,int num) {build(pre,rt,1,n,num);}
 30 //    void test(int x,int L,int R)
 31 //    {
 32 //        const int mid=(L+R)>>1;
 33 //        if (a[x].son[0]) test(a[x].son[0],L,mid);
 34 ////        cout<<L<<' '<<R<<' '<<a[x].cnt<<endl;
 35 //        if (a[x].son[1]) test(a[x].son[1],mid+1,R);
 36 //    }
 37 //    void test(int x) {test(x,1,n);}
 38 }smt;
 39 
 40 struct Edge{int to,next;}edge[maxn<<1];int first[maxn],le=2;
 41 void in(int x,int y) {Edge &e=edge[le];e.to=y;e.next=first[x];first[x]=le++;}
 42 void insert(int x,int y) {in(x,y);in(y,x);}
 43 
 44 int a[maxn],b[maxn],lb;
 45 int fa[maxn][22],dep[maxn],rt[maxn];
 46 void dfs(int x,int f)
 47 {
 48     fa[x][0]=f;dep[x]=dep[f]+1;
 49     for (int j=1;j<=18;j++)
 50         fa[x][j]=fa[fa[x][j-1]][j-1];
 51     smt.build(rt[f],rt[x],a[x]);
 52     for (int i=first[x];i;i=edge[i].next)
 53     {
 54         const Edge &e=edge[i];if (e.to==f) continue;
 55         dfs(e.to,x);
 56     }
 57 }
 58 void pre() {dfs(1,0);}
 59 
 60 int lca(int x,int y)
 61 {
 62     if (dep[x]<dep[y]) {int t=x;x=y;y=t;}
 63     for (int i=18;i>=0;i--) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
 64     if (x==y) return x;
 65     for (int i=18;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
 66     return fa[x][0];
 67 }
 68 
 69 int main()
 70 {
 71     scanf("%d%d",&n,&m);
 72     for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
 73     lb=n;sort(b+1,b+1+lb);lb=unique(b+1,b+1+lb)-b-1;
 74     for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+lb,a[i])-b;
 75     
 76     smt.clear(lb);
 77     for (int i=1,x,y;i<n;i++)
 78     {
 79         scanf("%d%d",&x,&y);
 80         insert(x,y);
 81     }
 82     pre();
 83 //    for (int i=1;i<=n;i++) smt.test(rt[i]),cout<<endl;
 84     int last=0;
 85     for (int i=1,x,y,K;i<=m;i++)
 86     {
 87         scanf("%d%d%d",&x,&y,&K);
 88         x^=last;
 89         int z=lca(x,y),w=fa[z][0],L=1,R=lb,tmp;
 90 //        cout<<x<<' '<<y<<' '<<z<<' '<<w<<endl;
 91         x=rt[x];y=rt[y];z=rt[z];w=rt[w];
 92         while (L<R)
 93         {
 94             if ((tmp=smt.a[smt.a[x].son[0]].cnt+smt.a[smt.a[y].son[0]].cnt
 95             -smt.a[smt.a[z].son[0]].cnt-smt.a[smt.a[w].son[0]].cnt)>=K)
 96                 x=smt.a[x].son[0],y=smt.a[y].son[0],z=smt.a[z].son[0],w=smt.a[w].son[0],R=(L+R)>>1;
 97             else x=smt.a[x].son[1],y=smt.a[y].son[1],z=smt.a[z].son[1],w=smt.a[w].son[1],K-=tmp,L=((L+R)>>1)+1;
 98 //            cout<<tmp<<' '<<K<<endl;
 99         }
100         printf("%d",(last=b[L]));
101         if (i<m) puts("");
102     }
103     return 0;
104 }
View Code

 

转载于:https://www.cnblogs.com/Blue233333/p/7927088.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值