题目大意:给一棵树,每个点一开始颜色互不相同,支持三个操作
1.将一个点到根的路径染成一种新的颜色
2.将一个新的点设为根,并将原来的根到这个点的路径染成一种新的颜色
3.查询一个子树(对于当前根)到根的路径期望颜色数
真TM是道神题,idea实在是太妙了
首先由于第2个操作的特殊性,我们可以发现,每种颜色在树上都是连续的,不会断开
于是第三个操作就变成了查询期望颜色段数
然后我们想象,如果一个点和他父亲结点的颜色不同,那就把他到他父亲的这条边权视为1,否则视为0
这样就变成了查询期望到根的路径和+1
然后我们看第一个操作对于这棵树上边权的影响:
这个点到根的路径全部变成0,与这条路径相邻的其他边都变成1
哇!太TM神奇了!
这是不是很像LCT的Access操作!
我们把边权为0的看做实边,边权为1的看做虚边,就和LCT一模一样
那么假设我们维护一个LCT,这样就可以在logn的时间内知道要修改哪些边权了!要修改的边的数量也是logn级别的!
于是我们可以在最开始树链剖分一下,用一颗线段树维护每个点到当前根的颜色段数
当修改一个边权的时候,相当于对这颗子树进行修改,那就在线段树上改一下就行了!
查询的时候,只需要用子树的和除以子树大小+1就好辣!
等等!
他还会换根?!那这子树怎么维护?!
其实也可以维护啦!如果做过“BZOJ3083 遥远的国度”就知道啦,只需要分类讨论一下就好了,当前的子树最多在原来的序列中被切成两段,不影响时间复杂度的!
具体可以看代码:
#include
#include
#include
#include
#define N 200010
using namespace std;
unsigned int to[N],nxt[N],pre[N],cnt;
unsigned int root=1;
void ae(unsigned int ff,unsigned int tt)
{
cnt++;
to[cnt]=tt;
nxt[cnt]=pre[ff];
pre[ff]=cnt;
}
unsigned int Siz[N],d[N],zs[N],FA[N];
unsigned int fa[N];
void build1(unsigned int x)
{
unsigned int i,j;
Siz[x]=1;
unsigned int maxn=0,maxb=0;
for(i=pre[x];i;i=nxt[i])
{
j=to[i];
if(j==fa[x]) continue;
FA[j]=fa[j]=x;
d[j]=d[x]+1;
build1(j);
Siz[x]+=Siz[j];
if(Siz[j]>maxn)
{
maxn=Siz[j];
maxb=j;
}
}
zs[x]=maxb;
}
unsigned int top[N],sit[N],fan[N],cn;
void make(unsigned int x,unsigned int tt)
{
cn++;
sit[x]=cn;fan[cn]=x;
top[x]=tt;
if(zs[x]) make(zs[x],tt);
unsigned int i,j;
for(i=pre[x];i;i=nxt[i])
{
j=to[i];
if(j==zs[x]||j==fa[x]) continue;
make(j,j);
}
}
unsigned int GET(unsigned int x,unsigned int y)
{
while(top[y]!=top[x])
{
if(FA[top[y]]==x) return top[y];
y=FA[top[y]];
}
return fan[sit[x]+1];
}
unsigned int ch[N][2],L[N],R[N];
unsigned int n,m;
bool rev[N];
void pup(unsigned int x)
{
if(!ch[x][0]) L[x]=x;
else L[x]=L[ch[x][0]];
if(!ch[x][1]) R[x]=x;
else R[x]=R[ch[x][1]];
}
void pudrev(unsigned int x)
{
if(!x) return;
swap(ch[x][0],ch[x][1]);
swap(L[x],R[x]);
rev[x]^=1;
}
void pud(unsigned int x)
{
if(rev[x])
{
pudrev(ch[x][0]);
pudrev(ch[x][1]);
rev[x]=false;
}
}
bool isroot(unsigned int x)
{
return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}
void rotate(unsigned int x)
{
unsigned int y=fa[x],z=fa[y];
if(!isroot(y))
{
if(ch[z][0]==y) ch[z][0]=x;
else ch[z][1]=x;
}
unsigned int l=0,r;
if(ch[y][1]==x) l=1;r=l^1;
fa[ch[x][r]]=y;
fa[y]=x;
fa[x]=z;
ch[y][l]=ch[x][r];
ch[x][r]=y;
pup(y);//pup(x);
}
unsigned int q[N],tt;
unsigned int l[N<<2],r[N<<2],sum[N<<2],t[N<<2];
void Pup(unsigned int x){sum[x]=sum[x<<1]+sum[x<<1|1];}
void Pud(unsigned int x)
{
if(!t[x]) return;
t[x<<1]+=t[x];t[x<<1|1]+=t[x];
sum[x<<1]+=(r[x<<1]-l[x<<1]+1)*t[x];
sum[x<<1|1]+=(r[x<<1|1]-l[x<<1|1]+1)*t[x];
t[x]=0;
}
void build(unsigned int now,unsigned int ll,unsigned int rr)
{
l[now]=ll;r[now]=rr;
if(ll==rr)
{
sum[now]=d[fan[ll]];
return;
}
unsigned int mid=(ll+rr)>>1;
build(now<<1,ll,mid);
build(now<<1|1,mid+1,rr);
Pup(now);
}
void change(unsigned int now,unsigned int ll,unsigned int rr,unsigned int v)
{
if(l[now]==ll&&r[now]==rr)
{
t[now]+=v;
sum[now]+=(r[now]-l[now]+1)*v;
return;
}
Pud(now);
unsigned int mid=(l[now]+r[now])>>1;
if(rr<=mid) change(now<<1,ll,rr,v);
else if(ll>mid) change(now<<1|1,ll,rr,v);
else change(now<<1,ll,mid,v),change(now<<1|1,mid+1,rr,v);
Pup(now);
}
unsigned int check(unsigned int now,unsigned int ll,unsigned int rr)
{
if(l[now]==ll&&r[now]==rr) return sum[now];
Pud(now);
unsigned int mid=(l[now]+r[now])>>1;
if(rr<=mid) return check(now<<1,ll,rr);
else if(ll>mid) return check(now<<1|1,ll,rr);
else return check(now<<1,ll,mid)+check(now<<1|1,mid+1,rr);
}
void splay(unsigned int x)
{
unsigned int xx=x;tt=0;
while(!isroot(xx)) tt++,q[tt]=xx,xx=fa[xx];
tt++;q[tt]=xx;
while(tt) pud(q[tt]),tt--;
while(!isroot(x))
{
unsigned int y=fa[x],z=fa[y];
if(!isroot(y))
{
if(ch[z][0]==y^ch[y][0]==x) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void changeit(unsigned int x,unsigned int v)
{
if(x==root) change(1,1,n,v);
else if(sit[root]>=sit[x]&&sit[x]+Siz[x]>=sit[root]+Siz[root])
{
x=GET(x,root);
if(sit[x]>1)
change(1,1,sit[x]-1,v);
if(sit[x]+Siz[x]-1=sit[x]&&sit[x]+Siz[x]>=sit[root]+Siz[root])
{
double tmp=0;
x=GET(x,root);
if(sit[x]>1)
tmp+=check(1,1,sit[x]-1);
if(sit[x]+Siz[x]-1