LCT+树剖+线段树+dfs序--bzoj3779: 重组病毒

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sizeof_you/article/details/84400133

传送门
一道数据结构综合神题(敲的我手都要断了

首先看三个操作,每次一个新病毒会感染一条链,而每个点用的时间就是到根的链上不同病毒的数量和,查询的时候相当于查整个子树的时间和。

很重要的一个思想是,当一个新病毒感染时,就像 L C T LCT 里的 a c c e s s access 一样,当一条虚边变成实边,说明它遇到了一个不一样的病毒,就要对它祖宗的另外一些子树 s u m + 1 sum+1 ,这个的实现可以是先给它的祖宗 + 1 +1 ,然后给它的子树 1 -1

第二个换根操作其实就是bzoj3083那道题
因为原树形态不变,子树的 d f s dfs 序最多由两个区间构成,所以只需要对操作点和当前根进行分类讨论就好了。 d f s dfs 序可以用树剖+线段树来维护。

所以这道题就可以先树剖一下,然后用线段树维护 d f s dfs 序的改变,在 l c t lct 上进行操作就好了

放上没有超过 200 200 行的代码(我没压行:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100005
#define LL long long
#define ls ch[x][0]
#define rs ch[x][1]
#define lls cur<<1
#define rrs cur<<1|1
#define qlen(x) (node[x].r-node[x].l+1)
using namespace std;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar(); 
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

int n,m,f[N],ch[N][2],rev[N],tl[N],tr[N],root=1;
char s[10];

inline int get(int x){return ch[f[x]][1]==x;}
inline void update(int x){
	if(!ls) tl[x]=x; else tl[x]=tl[ls];
	if(!rs) tr[x]=x; else tr[x]=tr[rs];
}
inline void rever(int x){rev[x]^=1;swap(ls,rs);swap(tl[x],tr[x]);}
inline void pushdown(int x){
	if(rev[x]){
		if(ls) rever(ls);
		if(rs) rever(rs);
		rev[x]=0;
	}
}
inline int isroot(int x){return ch[f[x]][0]!=x && ch[f[x]][1]!=x;}
void pushup(int x){if(!isroot(x)) pushup(f[x]);pushdown(x);}
inline void rotate(int x){
	int old=f[x],oldf=f[old],wh=get(x);
	if(!isroot(old)) ch[oldf][get(old)]=x;
	ch[old][wh]=ch[x][wh^1]; f[ch[x][wh^1]]=old;
	f[x]=oldf; f[old]=x; ch[x][wh^1]=old;
	update(old); update(x);
}
inline void splay(int x){
	pushup(x);
	for(;!isroot(x);rotate(x))
		if(!isroot(f[x])) rotate(get(x)==get(f[x])?f[x]:x);
}
//以上为LCT 
int cnt,head[N],dep[N],top[N],son[N],dfn[N],siz[N],fa[N],rk[N],num;

struct EDGE{
	int to,nxt;
}edge[N<<1];
inline void add(int x,int y){
	edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}

void dfs1(int u,int fat){
	siz[u]=1; int maxson=-1;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to; if(v==fat) continue;
		dep[v]=dep[u]+1; f[v]=fa[v]=u; dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>maxson) maxson=siz[v],son[u]=v;
	}
}

void dfs2(int u,int t){
	dfn[u]=++num; rk[num]=u; top[u]=t;
	if(!son[u]) return;
	dfs2(son[u],t);
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(!dfn[v]) dfs2(v,v);
	}
}

inline int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<=dep[y]?x:y;
}
//树剖 
inline int find(int x,int y){//求离x最近的在x到root上的点 
	while(top[x]!=top[y]){
		if(fa[top[y]]==x) return top[y];
		y=fa[top[y]];
	}
	return rk[dfn[x]+1];
}
 
struct Node{
	int l,r; LL sum,lazy;
}node[N<<2];
inline void Spushup(int cur){node[cur].sum=node[lls].sum+node[rrs].sum;}
inline void Spushdown(int cur){
	if(!node[cur].lazy) return ;
	node[lls].lazy+=node[cur].lazy,node[rrs].lazy+=node[cur].lazy;
	node[lls].sum+=1LL*qlen(lls)*node[cur].lazy,node[rrs].sum+=1LL*qlen(rrs)*node[cur].lazy;
	node[cur].lazy=0;
}

void build(int cur,int L,int R){
	if(L==R){
		node[cur].l=node[cur].r=L; node[cur].sum=dep[rk[L]];
		return;
	}
	int mid=(L+R)>>1;
	build(lls,L,mid); build(rrs,mid+1,R);
	node[cur].l=node[lls].l,node[cur].r=node[rrs].r;
	Spushup(cur);
}

void Supdate(int cur,int L,int R,int c){
	if(L<=node[cur].l && node[cur].r<=R){
		node[cur].lazy+=c; node[cur].sum+=1LL*qlen(cur)*c;
		return;
	}
	Spushdown(cur);
	int mid=(node[cur].l+node[cur].r)>>1;
	if(L<=mid) Supdate(lls,L,R,c);
	if(mid<R) Supdate(rrs,L,R,c);
	Spushup(cur);
}

LL Squery(int cur,int L,int R){
	if(L<=node[cur].l && node[cur].r<=R) return node[cur].sum;
	Spushdown(cur);
	int mid=(node[cur].l+node[cur].r)>>1;LL res=0;
	if(L<=mid) res+=Squery(lls,L,R);
	if(mid<R) res+=Squery(rrs,L,R);
	return res;
}
//以上为线段树 

inline void change(int x,int c){
	if(x==root) Supdate(1,1,n,c);
	else if(LCA(root,x)==x){
		x=find(x,root);
		if(dfn[x]>1) Supdate(1,1,dfn[x]-1,c);
		if(dfn[x]+siz[x]-1<n) Supdate(1,dfn[x]+siz[x],n,c);
	}
	else Supdate(1,dfn[x],dfn[x]+siz[x]-1,c);
}

inline double solve(int x){
	if(x==root) return 1.0*Squery(1,1,n)/(double)n;
	else if(LCA(root,x)==x){
		double tmp=0;
		x=find(x,root);
		if(dfn[x]>1) tmp+=Squery(1,1,dfn[x]-1);
		if(dfn[x]+siz[x]-1<n) tmp+=Squery(1,dfn[x]+siz[x],n);
		return tmp/(double)(n-siz[x]);
	}
	else return 1.0*Squery(1,dfn[x],dfn[x]+siz[x]-1)/(double)siz[x];
}
//以上为修改和查询操作

inline void access(int x){
	for(int y=0;x;y=x,x=f[x]) {
		splay(x);
		if(rs) change(tl[rs],1);
		rs=y;
		if(y) change(tl[y],-1); update(x);
	}
}
inline void makeroot(int x){
	access(x); splay(x); rever(x);
}
 
int main(){
	n=rd(); m=rd();
	for(int i=1;i<n;i++){
		int x=rd(),y=rd();
		add(x,y); add(y,x);
	}
	dfs1(root,root); dfs2(root,root);
	for(int i=1;i<=n;i++) tl[i]=tr[i]=i;
	build(1,1,n);
	while(m--){
		scanf("%s",s); int x=rd();
		if(s[2]=='L') access(x);
		else if(s[2]=='C'){
			makeroot(x); root=x;
		}
		else printf("%.10lf\n",solve(x)+1.0);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/sizeof_you/article/details/84400133
今日推荐