bzoj3779-重组病毒
一颗\(n\)个点的树,初始颜色都不同,初始根为1。定义一个点到根的代价为这个点到根路径上不同颜色个数。有\(m\)个操作,分三种:
- 将一个点到根路径上的所有点颜色改为一种新的颜色
- 询问一个点的子树的所有点到根的代价和
- 对点\(x\)进行操作1后把根换成\(x\)
\(n,m\le 10^5\)。
分析
那个修改看着是不是很像access操作呢!换根操作之前要先access就是link-cut tree的makeroot呀!
首先,令1的深度为1,那么初始所有点的代价就是他们的深度。我们用lct来维护这个树的结构,支持换根即可。所以一切的关键都集中在了access上。
access其实就是实虚边切换的操作。注意到代价其实就是点到根路径上虚边个数+1,所以如果我们把一条实边改成虚边,那么整颗子树的答案加一,虚边改实边整个子树答案减一,用线段树维护一下dfs序就好啦。
等等!我们不是换了根吗,怎么用dfs序维护子树呢?画一画图,设当前点为\(x\),发现:
- \(root=x\),那么就是整个树
- \(root\)在\(x\)的子树中,那么就是整棵树除去\(x\)的root那颗子树
- 否则就是原来的子树
所以dfs序依然可以维护,只是要分情况讨论一下。
代码
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long giant;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=1e5+10;
const int maxj=18;
int n,m,root=1,first[maxn],second[maxn],dft=0,f[maxn][maxj],dep[maxn];
struct node {
int ch[2],fa;
bool rev;
} t[maxn];
struct SGT {
giant t[maxn<<2],tag[maxn<<2];
void push(int x,int L,int mid,int R) {
if (!tag[x]) return;
tag[x<<1]+=tag[x],tag[x<<1|1]+=tag[x];
t[x<<1]+=(mid-L+1)*tag[x];
t[x<<1|1]+=(R-mid)*tag[x];
tag[x]=0;
}
void add(int x,int L,int R,int l,int r,giant d) {
if (L==l && R==r) {
tag[x]+=d;
t[x]+=(R-L+1)*d;
return;
}
int mid=(L+R)>>1;
push(x,L,mid,R);
if (r<=mid) add(x<<1,L,mid,l,r,d); else
if (l>mid) add(x<<1|1,mid+1,R,l,r,d); else
add(x<<1,L,mid,l,mid,d),add(x<<1|1,mid+1,R,mid+1,r,d);
t[x]=t[x<<1]+t[x<<1|1];
}
void add(int l,int r,giant d) {
if (l>r) return;
add(1,1,n,l,r,d);
}
giant query(int x,int L,int R,int l,int r) {
if (L==l && R==r) return t[x];
int mid=(L+R)>>1;
push(x,L,mid,R);
if (r<=mid) return query(x<<1,L,mid,l,r);
if (l>mid) return query(x<<1|1,mid+1,R,l,r); else
return query(x<<1,L,mid,l,mid)+query(x<<1|1,mid+1,R,mid+1,r);
}
giant query(int l,int r) {
if (l>r) return 0ll;
return query(1,1,n,l,r);
}
} sgt;
vector<int> g[maxn];
void add(int x,int y) {g[x].push_back(y);}
void dfs(int x,int fa) {
first[x]=++dft;
dep[x]=dep[fa]+1;
f[x][0]=fa;
sgt.add(first[x],first[x],dep[x]);
for (int v:g[x]) if (v!=fa) t[v].fa=x,dfs(v,x);
second[x]=dft;
}
int jump(int x,int y) {
if (!y) return x;
for (int j=0;j<maxj;++j) if ((y>>j)&1) x=f[x][j];
return x;
}
bool insub(int x,int y) {
return first[x]<=first[y] && first[y]<=second[x];
}
double query(int x) {
if (root==x) {
return (double)sgt.query(1,n)/n;
} else if (insub(x,root)) {
int p=jump(root,dep[root]-dep[x]-1);
double ret=sgt.query(1,first[p]-1)+sgt.query(second[p]+1,n);
ret/=(double)(first[p]-1+n-second[p]);
return ret;
} else {
return (double)sgt.query(first[x],second[x])/(second[x]-first[x]+1);
}
}
bool rson(int x) {
return t[t[x].fa].ch[1]==x;
}
bool isroot(int x) {
return !x || t[t[x].fa].ch[rson(x)]!=x;
}
void push(int x) {
if (t[x].rev) {
swap(t[x].ch[0],t[x].ch[1]);
if (t[x].ch[0]) t[t[x].ch[0]].rev^=true;
if (t[x].ch[1]) t[t[x].ch[1]].rev^=true;
t[x].rev=false;
}
}
void down(int x) {
if (!isroot(x)) down(t[x].fa);
push(x);
}
void rotate(int x) {
int f=t[x].fa,d=rson(x),c=t[x].ch[d^1];
if (!isroot(f)) t[t[f].fa].ch[rson(f)]=x;
if (c) t[c].fa=f;
t[x].fa=t[f].fa,t[f].fa=x,t[f].ch[d]=c,t[x].ch[d^1]=f;
}
void splay(int x) {
down(x);
while (!isroot(x)) {
if (isroot(t[x].fa)) rotate(x); else {
if (rson(x)==rson(t[x].fa)) rotate(t[x].fa),rotate(x); else
rotate(x),rotate(x);
}
}
}
void change(int x,giant d) {
if (x==root) sgt.add(1,n,d); else
if (insub(x,root)) {
int p=jump(root,dep[root]-dep[x]-1);
sgt.add(1,first[p]-1,d);
sgt.add(second[p]+1,n,d);
} else {
sgt.add(first[x],second[x],d);
}
}
int left(int x) {
for (push(x);t[x].ch[0];x=t[x].ch[0],push(x));
return x;
}
void ace(int x) {
for (int last=0;x;x=t[last=x].fa) {
splay(x);
giant tmp=sgt.query(first[x],first[x]);
if (t[x].ch[1]) change(left(t[x].ch[1]),1);
if (last) change(left(last),-1);
t[x].ch[1]=last;
}
}
void cent(int x) {
root=x;
splay(x);
t[x].rev^=true;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("my.out","w",stdout);
#endif
n=read(),m=read();
for (int i=1;i<n;++i) {
int x=read(),y=read();
add(x,y),add(y,x);
}
dfs(1,1);
for (int j=1;j<maxj;++j) for (int i=1;i<=n;++i) f[i][j]=f[f[i][j-1]][j-1];
while (m--) {
static char ord[20];
scanf("%s",ord);
int x=read();
if (ord[2]=='Q') {
double ans=query(x);
printf("%.10lf\n",ans);
} else {
ace(x);
if (ord[2]=='C') cent(x);
}
}
return 0;
}