[BZOJ3779]重组病毒

题面描述

穷哭了

思路

难吗?难码.

首先观察一下操作一,就是一个access,但是要改变子树啊,LCT不缁瓷,所以线段树稍微维护一下。

怎么维护是一个大难点啊。

是要分类讨论的。

先找出实右子树在原数上的根 x x x

情况

  1. r t = x rt=x rt=x,直接修改整颗树。
  2. r t rt rt在子树中,令 y = r t y=rt y=rt,跳到 x x x的儿子上,由于 x x x的整颗子树,都不用经过x就可以到达 y y y,即可证明正确性,修改除此以外部分。
  3. 不在树中,修改这棵子树即可。

询问也是类似的。

如何找到在线段树找到子树的头和尾呢?

d f s dfs dfs序是一个好办法。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define ll long long 
#define gc getchar()
using namespace std;
const int N=1e5+10;
inline void qr(int &x)
{
	x=0;int f=1;char c=gc;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
	x*=f;
}
int rt,dep[N],fa[N][18],tmp[N],st[N],ed[N],n,m,bin[18],tp;
inline void jump(int &a,int h){for(int i=0;h;i++)if(h&bin[i])h^=bin[i],a=fa[a][i];}
struct SGT{int l,r,ad,c;ll s;}tr[N<<2];
inline void update(int p){tr[p].s=tr[p<<1].s+tr[p<<1|1].s;}
inline void cad(int p,int ad){tr[p].s+=tr[p].c*ad,tr[p].ad+=ad;}
void pud(int p){if(tr[p].ad)cad(p<<1,tr[p].ad),cad(p<<1|1,tr[p].ad),tr[p].ad=0;}
void build(int p,int l,int r)
{
	if(l==r){tr[p].l=tr[p].r=l;tr[p].s=tmp[l];tr[p].c=1;tr[p].ad=0;return ;}
	tr[p].l=l;tr[p].r=r;int mid=(l+r)>>1;
	build(p<<1,l,mid);build(p<<1|1,mid+1,r);
	update(p);
	tr[p].c=tr[p<<1].c+tr[p<<1|1].c;
}
void change(int p,int l,int r,int ad)
{
	//if(l>r)return ;
	if(l<=tr[p].l&&r>=tr[p].r){cad(p,ad);return ;}
	pud(p);int mid=(tr[p].l+tr[p].r)>>1;
	if(l<=mid)change(p<<1,l,r,ad);
	if(r>mid)change(p<<1|1,l,r,ad);
	update(p);
}
void change(int x,int ad)
{
	if(x==rt)change(1,1,n,ad);
	else if(st[rt]>st[x]&&st[rt]<=ed[x])
	{
		int y=rt;jump(y,dep[rt]-dep[x]-1);//倍增跳到x的儿子
		if(st[y]!=1)change(1,1,st[y]-1,ad);if(ed[y]!=n)change(1,ed[y]+1,n,ad);
	}
	else change(1,st[x],ed[x],ad);
}
ll query(int p,int l,int r)
{
	if(l<=tr[p].l&&r>=tr[p].r)return tr[p].s;
	ll val=0;int mid=(tr[p].l+tr[p].r)>>1;pud(p);
	if(l<=mid)val+=query(p<<1,l,r);
	if(r>mid)val+=query(p<<1|1,l,r);
	return val;
}
double query(int x)
{
	if(x==rt)return 1.0*query(1,1,n)/n;
	else if(st[rt]>st[x]&&st[rt]<=ed[x])
	{
		int y=rt;jump(y,dep[y]-dep[x]-1);
		ll s1=0,s2=0;
		if(st[y]!=1)s1=query(1,1,st[y]-1);if(ed[y]!=n)s2=query(1,ed[y]+1,n);
		return 1.0*(s1+s2)/(st[y]-1+n-ed[y]);
	}
	else return 1.0*query(1,st[x],ed[x])/(ed[x]-st[x]+1);
}
struct LCT{int f,son[2];bool rv;}t[N];
inline bool nroot(int p){return t[t[p].f].son[0]==p||t[t[p].f].son[1]==p;}
void crv(int p){if(!p)return ;t[p].rv^=1;swap(t[p].son[0],t[p].son[1]);}
void pushdown(int p){if(t[p].rv)crv(t[p].son[0]),crv(t[p].son[1]),t[p].rv=0;}
void rotate(int p,int w)
{
	int f=t[p].f,gf=t[f].f;
	int r=t[p].son[w],R=f;t[R].son[w^1]=r;if(r)t[r].f=R;
	r=p;R=gf;if(nroot(f))t[R].son[t[R].son[1]==f]=r;t[r].f=R;
	r=f;R=p;t[R].son[w]=r;t[r].f=R;
}
void sdfs(int p){if(nroot(p))sdfs(t[p].f);pushdown(p);}
void splay(int p)
{
	for(sdfs(p);nroot(p);)
	{
		int f=t[p].f,gf=t[f].f;
		if(!nroot(f))rotate(p,t[f].son[0]==p);
		else
		{
			if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
			else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
			else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
			else rotate(f,0),rotate(p,0);
		}
	}
}
int find(int x){pushdown(x);while(t[x].son[0])x=t[x].son[0],pushdown(x);return x;}
void access(int x)
{
	for(int y=0;x;x=t[y=x].f)
	{
		splay(x);if(t[x].son[1])change(find(t[x].son[1]),1);
		t[x].son[1]=y;
		if(y)change(find(y),-1);
	}
}
void makeroot(int x){access(x);splay(x);crv(x);rt=x;}
struct edge{int y,next;}a[N<<1];int len,last[N];
void ins(int x,int y){a[++len]=(edge){y,last[x]};last[x]=len;}
void dfs(int x)
{
	st[x]=++tp;t[x].f=fa[x][0],tmp[tp]=tmp[st[fa[x][0]]]+1;
	for(int i=1;i<=17;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int k=last[x],y;k;k=a[k].next)
	{
		if((y=a[k].y)==t[x].f)continue;
		dep[y]=dep[x]+1;fa[y][0]=x;dfs(y);
	}
	ed[x]=tp;
}
char s[N][20];int qx[N];
int main()
{
	//freopen("recompile2.in","r",stdin);
	bin[0]=1;
	for(int i=1;i<=17;i++)bin[i]=bin[i-1]<<1;
	qr(n),qr(m);
	for(int i=1,x,y;i<n;i++)qr(x),qr(y),ins(x,y),ins(y,x);
	rt=1;tp=0;dfs(1);
	build(1,1,n);
	for(int i=1;i<=m;i++){scanf("%s",s[i]+1);qr(qx[i]);}
	bool bk=0;
	for(int i=m;i>=1;i--)if(s[i][3]=='Q'){m=i;bk=1;break;}
	if(!bk)m=0;
	for(int i=1;i<=m;i++)
	{
		int x=qx[i];
		if(s[i][3]=='L')access(x);
		else if(s[i][3]=='C')makeroot(x);
		else printf("%.10lf\n",query(x));
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值