[spojQTREE6]Query on a tree VI

考虑如下构造:

新建一条边$(0,1)$,并将原树以0为根建树,记$fa_{x}$为$x$的父亲(其中$1\le x\le n$)

维护两棵森林,分别记作$T_{0/1}$,每一条边恰属于一棵,其中$(x,fa_{x})\in T_{0}$当且仅当$x$为白色点

此时,考虑节点$x$的答案(不妨假设$x$为白色点),即是$T_{0}$去掉$x$所在连通块根节点(深度最小的节点)后所在连通块子树大小,正确性显然

对这两棵森林使用LCT维护,考虑修改和查询:

1.修改时,即增加并删除一条边

2.查询时,不妨假设$x$为白色,将$T_{0}$中$x$所在连通块内深度最小的点make_root后,不难发现即求该点右儿子子树范围内的点数,显然容易维护("子树范围"定义以及如何维护见QTREE5

考虑如何快速找到该点,考虑保持树的形态(注意初始状态),即在make_root时不rev,那么find_root的结果即为该最浅的点

这样在加边和删边时并不会产生影响(其实通常的LCT在加边和删边时也并不需要rev),查询时的做法上面已经给出,即find_root一下即可

时间复杂度为$o(n\log n)$,可以通过

[spojQTREE6]Query on a tree VI
  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define N 100005
  4 vector<int>v[N];
  5 int n,m,x,y,f[N],col[N];
  6 struct LCT{
  7     int fa[N],vir[N],sz[N],ch[N][2];
  8     LCT(){
  9         for(int i=1;i<N;i++)sz[i]=1;
 10     }
 11     bool check(int k){
 12         return ((ch[fa[k]][0]!=k)&&(ch[fa[k]][1]!=k));
 13     }
 14     int which(int k){
 15         return ch[fa[k]][1]==k;
 16     }
 17     void up(int k){
 18         sz[k]=sz[ch[k][0]]+sz[ch[k][1]]+vir[k]+1;
 19     }
 20     void add_vir(int k){
 21         vir[fa[k]]+=sz[k]; 
 22     }
 23     void del_vir(int k){
 24         vir[fa[k]]-=sz[k];
 25     }
 26     void rotate(int k){
 27         int f=fa[k],g=fa[f],p=which(k);
 28         fa[k]=g;
 29         if (!check(f))ch[g][which(f)]=k;
 30         fa[ch[k][p^1]]=f,ch[f][p]=ch[k][p^1];
 31         fa[f]=k,ch[k][p^1]=f;
 32         up(f),up(k);
 33     }
 34     void splay(int k){
 35         for(int i=fa[k];!check(k);i=fa[k]){
 36             if (!check(i)){
 37                 if (which(i)==which(k))rotate(i);
 38                 else rotate(k);
 39             }
 40             rotate(k);
 41         }
 42     }
 43     void access(int k){
 44         int lst=0;
 45         while (k){
 46             splay(k);
 47             if (ch[k][1])add_vir(ch[k][1]);
 48             if (lst)del_vir(lst);
 49             ch[k][1]=lst,up(k);
 50             lst=k,k=fa[k];
 51         }
 52     }
 53     void make_root(int k){
 54         access(k);
 55         splay(k);
 56     }
 57     int find_root(int k){
 58         access(k);
 59         splay(k);
 60         while (ch[k][0])k=ch[k][0];
 61         splay(k);
 62         return k;
 63     }
 64     void add(int x,int y){
 65         make_root(x);
 66         make_root(y);
 67         fa[y]=x,add_vir(y),up(x);
 68     }
 69     void del(int x,int y){
 70         make_root(x);
 71         access(y);
 72         fa[y]=ch[x][1]=0,up(x);
 73     }
 74     int query(int k){
 75         k=find_root(k);
 76         return sz[ch[k][1]];
 77     }
 78 }T[2];
 79 void dfs(int k,int fa){
 80     f[k]=fa;
 81     for(int i=0;i<v[k].size();i++)
 82         if (v[k][i]!=fa)dfs(v[k][i],k);
 83 }
 84 int main(){
 85     scanf("%d",&n);
 86     for(int i=1;i<n;i++){
 87         scanf("%d%d",&x,&y);
 88         v[x].push_back(y);
 89         v[y].push_back(x);
 90     }
 91     dfs(1,n+1);
 92     for(int i=1;i<=n;i++){
 93         col[i]=1;
 94         T[1].add(f[i],i);
 95     }
 96     scanf("%d",&m);
 97     for(int i=1;i<=m;i++){
 98         scanf("%d%d",&x,&y);
 99         if (!x)printf("%d\n",T[col[y]].query(y));
100         else{
101             T[col[y]].del(f[y],y);
102             col[y]^=1;
103             T[col[y]].add(f[y],y);
104         }
105     }
106     return 0;
107 }
View Code

 

上一篇:LCT倾情压行板子


下一篇:蓝桥每日真题之城邦