BZOJ3779: 重组病毒

14 篇文章 0 订阅
10 篇文章 0 订阅

题目大意:给一棵树,每个点一开始颜色互不相同,支持三个操作

                 1.将一个点到根的路径染成一种新的颜色

                 2.将一个新的点设为根,并将原来的根到这个点的路径染成一种新的颜色

                 3.查询一个子树(对于当前根)到根的路径期望颜色数

 

真TM是道神题,idea实在是太妙了

首先由于第2个操作的特殊性,我们可以发现,每种颜色在树上都是连续的,不会断开

于是第三个操作就变成了查询期望颜色段数

然后我们想象,如果一个点和他父亲结点的颜色不同,那就把他到他父亲的这条边权视为1,否则视为0

这样就变成了查询期望到根的路径和+1

然后我们看第一个操作对于这棵树上边权的影响:

这个点到根的路径全部变成0,与这条路径相邻的其他边都变成1

 

哇!太TM神奇了!

这是不是很像LCT的Access操作!

我们把边权为0的看做实边,边权为1的看做虚边,就和LCT一模一样

那么假设我们维护一个LCT,这样就可以在logn的时间内知道要修改哪些边权了!要修改的边的数量也是logn级别的!

于是我们可以在最开始树链剖分一下,用一颗线段树维护每个点到当前根的颜色段数

当修改一个边权的时候,相当于对这颗子树进行修改,那就在线段树上改一下就行了!

查询的时候,只需要用子树的和除以子树大小+1就好辣!

 

等等!

 

他还会换根?!那这子树怎么维护?!

其实也可以维护啦!如果做过“BZOJ3083 遥远的国度”就知道啦,只需要分类讨论一下就好了,当前的子树最多在原来的序列中被切成两段,不影响时间复杂度的!

 

具体可以看代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#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<n)
		change(1,sit[x]+Siz[x],n,v);
	}
	else change(1,sit[x],sit[x]+Siz[x]-1,v);
}
void access(unsigned int x)
{
	unsigned int t=0;
	while(x)
	{
		splay(x);
		if(ch[x][1]) changeit(L[ch[x][1]],1);
		ch[x][1]=t;
		if(t) changeit(L[t],-1);
		pup(x);
		t=x;
		x=fa[x];
	}
}
void makeroot(unsigned int x)
{
	access(x);
	splay(x);
	swap(ch[x][0],ch[x][1]);
	swap(L[x],R[x]);
	rev[x]^=1;
}
double checkit(unsigned int x)
{
	if(x==root) return (double)1.0*check(1,1,n)/n;
	else if(sit[root]>=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<n)
		tmp+=check(1,sit[x]+Siz[x],n);
		return (double)tmp/(n-Siz[x]);
	}
	else return (double)1.0*check(1,sit[x],sit[x]+Siz[x]-1)/Siz[x];
}
int main()
{
	scanf("%d%d",&n,&m);
	unsigned int i,j,x,y;
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		ae(x,y);ae(y,x);
	}
	build1(1);
	make(1,1);
	for(i=1;i<=n;i++)
	L[i]=R[i]=i;
	build(1,1,n);
	char s[21];
	unsigned int tot=0,st;
	//		st=clock();
	while(m--)
	{
		scanf("%s%d",s,&x);
		if(s[2]=='L') access(x);
		else if(s[2]=='C')
		{
			makeroot(x);
			root=x;
		}
		else printf("%.10lf\n",checkit(x)+1.0);
	}
}


 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值