题意:
给一棵树,每个点一开始颜色互不相同,支持三个操作
1. 将一个点到根的路径染成一种新的颜色
2. 将一个新的点设为根,并将原来的根到这个点的路径染成一种新的颜色
3. 查询一个子树(对于当前根)到根的路径期望颜色数
这道题是我迄今为止,做过的最难想,也最混乱的大数据结构题了,请诸位理智吸收。
在我们的第一意识中,最高级的数据结构,才应该是一道题最核心的地方,但是,可这样想就会在这道题上吃瘪。
我们观察题目,发现一开始每个点都有一个特定的颜色,而且每次修改,都是将一个点到根的路径打通。
这就很像是LCT中的access()操作,更可观的一个性质是,LCT恰好还是一个支持换根的树形结构。
好,这道题用肯定要LCT。
(可是LCT并不能做这道题)
在实际的做法中,这道题并不是以LCT为主的,换句话说,我们只是拿LCT当一个工具,来模拟操作的过程,用树链剖分协助来找到需要修改的子树以及维护子树的size,并且用线段树来维护每个点到根路径上的颜色树。
这样,问题三就是(子树每个点到根颜色数和/子树size)。
(那换根操作怎么办)
凉拌!我们画一张图来看看
假如一开始的跟是四号点(只是一个例子,和题目无关,题目里初始1为根),那么size[5]的值是7,如果将根换为7号点,如图
我们看,现在如果我们想获得5号点的size时,只需要用n-size[7]即可(并不是因为7是跟,而是因为7是和5原深度差1的点)。
我们多试试可以发现,换根后的子树信息有两种情况:
首先定义一个点的深度为最原始根的条件下的深度。
1. 要求的x点的子树信息,x点的深度比当前根的深度大,那么现在的子树信息即为原始的子树信息,这个规律在dfs序上也适用。
2. 当x点的深度比当前根的深度小,那么就由当前跟向上跳(可以用树链剖分的跳重链的手段在log的时间复杂度内实现),跳到x点的直接儿子处,记此点为y,则现在x的子树信息即为总的树的信息减去y的子树信息(对于“子树size”和“子树点到根路径颜色数和”全都适用)。
综上所述,其实一个点代表的子树只有两种情况,要么是原先的情况,要么是从整棵树抠出一小棵子树后的情况,而判断是哪种情况的标准,就是原始根条件下点的深度大小。此致,请注意数据结构应用的灵活性!
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #define ll long long 7 #define db double 8 #define lc(x) t[x][0] 9 #define rc(x) t[x][1] 10 using namespace std; 11 const int N=100005; 12 //树剖------------------------------------↓ 13 struct node{int y,nxt;}e[N*2]; 14 int n,m,ro,h[N],c=1,tp[N],pre[N],bg[N]; 15 int ed[N],tot,d[N],sz[N],son[N],pid[N]; 16 17 void add(int x,int y){ 18 e[++c]=(node){y,h[x]};h[x]=c; 19 e[++c]=(node){x,h[y]};h[y]=c; 20 } bool ispre(int x,int y){ 21 if(x==y) return 0; 22 return bg[x]<=bg[y]&&ed[y]<=ed[x]; 23 } void dfs1(int x){ 24 sz[x]=1; 25 for(int i=h[x],y;i;i=e[i].nxt) 26 if((y=e[i].y)!=pre[x]){ 27 pre[y]=x;d[y]=d[x]+1; 28 dfs1(y);sz[x]+=sz[y]; 29 if(sz[y]>sz[son[x]]) son[x]=y; 30 } return ; 31 } void dfs2(int x){ 32 bg[x]=++tot;pid[tot]=x; 33 if(son[x]) 34 tp[son[x]]=tp[x],dfs2(son[x]); 35 int p,y;for(int i=h[x],y;i;i=e[i].nxt) 36 if((y=e[i].y)!=pre[x]&&y!=son[x]) 37 tp[y]=y,dfs2(y);ed[x]=tot; 38 } int skip(int x,int y){ 39 for(;d[tp[x]]>d[y];x=pre[x]){ 40 x=tp[x];if(pre[x]==y) return x; 41 } return pid[bg[y]+1]; 42 } ll getsz(int x){ 43 if(x==ro) return n; 44 if(ispre(x,ro)){ 45 int y=skip(ro,x); 46 return n-sz[y]; 47 } else return sz[x]; 48 } 49 //树剖------------------------------------↑ 50 //线段树----------------------------------↓ 51 struct segtree{ 52 int l,r,ls,rs;ll sm,tag; 53 }sgt[N<<2];int cnt=0,rt; 54 void sg_pushup(int cur){ 55 int lson=sgt[cur].ls,rson=sgt[cur].rs; 56 sgt[cur].sm=sgt[lson].sm+sgt[rson].sm; 57 } void sg_pushdown(int cur){ 58 if(sgt[cur].tag){ 59 int ls=sgt[cur].ls,rs=sgt[cur].rs; 60 sgt[ls].sm+=sgt[cur].tag*(sgt[ls].r-sgt[ls].l+1); 61 sgt[rs].sm+=sgt[cur].tag*(sgt[rs].r-sgt[rs].l+1); 62 sgt[ls].tag+=sgt[cur].tag; 63 sgt[rs].tag+=sgt[cur].tag;sgt[cur].tag=0; 64 } return ; 65 } void sg_build(int l,int r,int cur){ 66 sgt[cur].l=l;sgt[cur].r=r;if(l==r){ 67 sgt[cur].ls=sgt[cur].rs=-1; 68 sgt[cur].tag=sgt[cur].sm=0;return ; 69 } int mid=l+r>>1; 70 sgt[cur].ls=cnt++;sgt[cur].rs=cnt++; 71 sg_build(l,mid,sgt[cur].ls); 72 sg_build(mid+1,r,sgt[cur].rs); 73 sg_pushup(cur); 74 } void sg_upd(int l,int r,ll c,int cur){ 75 if(l<=sgt[cur].l&&sgt[cur].r<=r){ 76 sgt[cur].sm+=(sgt[cur].r-sgt[cur].l+1)*c; 77 sgt[cur].tag+=c;return ; 78 } int mid=sgt[cur].l+sgt[cur].r>>1; 79 sg_pushdown(cur); 80 if(l<=mid) sg_upd(l,r,c,sgt[cur].ls); 81 if(mid<r) sg_upd(l,r,c,sgt[cur].rs); 82 sg_pushup(cur); 83 } void sg_add(int x,ll c){ 84 if(x==ro) return sg_upd(1,tot,c,rt); 85 if(ispre(x,ro)){ 86 int y=skip(ro,x); 87 sg_upd(bg[1],ed[1],c,rt); 88 sg_upd(bg[y],ed[y],-c,rt); 89 } else sg_upd(bg[x],ed[x],c,rt); 90 } ll sg_que(int l,int r,int cur){ 91 if(l<=sgt[cur].l&&sgt[cur].r<=r) 92 return sgt[cur].sm;ll re=0; 93 int mid=sgt[cur].l+sgt[cur].r>>1; 94 sg_pushdown(cur); 95 if(l<=mid) re+=sg_que(l,r,sgt[cur].ls); 96 if(mid<r) re+=sg_que(l,r,sgt[cur].rs); 97 return re; 98 } ll sg_sum(int x){ 99 if(x==ro) return sgt[rt].sm; 100 ll re=0;if(ispre(x,ro)){ 101 int y=skip(ro,x); 102 re+=sg_que(bg[1],ed[1],rt); 103 re-=sg_que(bg[y],ed[y],rt); 104 } else re=sg_que(bg[x],ed[x],rt); 105 return re; 106 } 107 //线段树----------------------------------↑ 108 //LCT-------------------------------------↓ 109 int t[N][2],rev[N],lf[N],rf[N],fu[N]; 110 int s[N],top=0,k,id[N]; 111 void rever(int x){ 112 rev[x]^=1; 113 swap(rc(x),lc(x));swap(lf[x],rf[x]); 114 } void pushup(int x){ 115 lf[x]=rf[x]=id[x]; 116 if(lc(x)) lf[x]=lf[lc(x)]; 117 if(rc(x)) rf[x]=rf[rc(x)]; 118 } void pushdown(int x){ 119 int ls=lc(x),rs=rc(x); 120 if(rev[x]){ 121 if(lc(x)) rever(lc(x)); 122 if(rc(x)) rever(rc(x)); 123 } rev[x]=0;return; 124 } bool pdrt(int x){ 125 return lc(fu[x])!=x&&rc(fu[x])!=x; 126 } void rotate(int x){ 127 int y=fu[x];int z=fu[y]; 128 int dy=(rc(y)==x),dz=(rc(z)==y); 129 if(!pdrt(y)) t[z][dz]=x; 130 t[y][dy]=t[x][dy^1],fu[t[x][dy^1]]=y; 131 t[x][dy^1]=y;fu[y]=x;fu[x]=z; 132 pushup(y);pushup(x); 133 } void splay(int x){ 134 s[++top]=x; 135 for(int i=x;!pdrt(i);i=fu[i]) 136 s[++top]=fu[i];while(top) 137 pushdown(s[top--]); 138 while(!pdrt(x)){ 139 int y=fu[x];int z=fu[y];if(!pdrt(y)) 140 if(rc(y)==x^rc(z)==y) rotate(x); 141 else rotate(y);rotate(x); 142 } pushup(x); 143 } void access(int x){ 144 for(int i=0;x;x=fu[x]){ 145 splay(x);if(rc(x)) 146 sg_add(lf[rc(x)],1); 147 if(i) sg_add(lf[i],-1); 148 rc(x)=i;pushup(x);i=x; 149 } return ; 150 } void mkrt(int x){ 151 access(x),splay(x);rever(x); 152 } int fdrt(int x){ 153 access(x);splay(x); 154 while(lc(x)) pushdown(x),x=lc(x); 155 return x; 156 } void split(int x,int y){ 157 mkrt(x);access(y);splay(y); 158 } void link(int x,int y){ 159 mkrt(x);if(fdrt(y)!=x) fu[x]=y; 160 } void cut(int x,int y){ 161 mkrt(x); 162 if(fdrt(y)==x&&fu[x]==y&&!rc(x)) 163 fu[x]=t[y][0]=0;pushup(y); 164 } 165 //LCT-------------------------------------↑ 166 //主函数----------------------------------↓ 167 int main(){ 168 scanf("%d%d",&n,&m); 169 rt=cnt++;ro=1; 170 for(int i=1,x,y;i<n;i++) 171 scanf("%d%d",&x,&y),add(x,y); 172 pre[1]=0;d[1]=1;dfs1(1); 173 tp[1]=1;dfs2(1); 174 sg_build(1,tot,rt); 175 for(int i=1;i<=n;i++){ 176 lf[i]=rf[i]=id[i]=i; 177 if(pre[i]) fu[i]=pre[i],sg_add(i,1); 178 } char s[13];int x; 179 while(m--){ 180 scanf("%s%d",s,&x); 181 if(s[2]=='L') access(x),splay(x); 182 else if(s[2]=='C') mkrt(x),ro=x; 183 else{ 184 ll tp1=sg_sum(x); 185 ll tp2=getsz(x);tp1+=tp2; 186 printf("%.10lf\n",(db)tp1/(db)tp2); 187 } 188 } 189 return 0; 190 }