[BZOJ3779]重组病毒 题解 [LCT+dfs序]

BZOJ3779. 重组病毒

Description:

​ 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒。这种病毒的繁殖和变异能力极强。为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒。
实验在一个封闭的局域网内进行。局域网内有\(n\)台计算机,编号为\(1\)~\(n\)。一些计算机之间通过网线直接相连,形成树形的结构。局域网中有一台特殊的计算机,称之为核心计算机。根据一些初步的研究,研究员们拟定了一个一共\(m\)步的实验。实验开始之前,核心计算机的编号为\(1\),每台计算机中都有病毒的一个变种,而且每台计算机中的变种都不相同。实验中的每一步会是下面中的一种操作:
\(1\)\(RELEASE\) \(x\)
​ 在编号为x的计算机中植入病毒的一个新变种。这个变种在植入之前不存在于局域网中。
\(2\)\(RECENTER\) \(x\)
​ 将核心计算机改为编号为\(x\)的计算机。但是这个操作会导致原来核心计算机中的病毒产生新变种,并感染过来。换言之,假设操作前的核心计算机编号为\(y\),相当于在操作后附加了一次\(RELEASE\) \(y\)的操作。
根据研究的结论,在植入一个新变种时,病毒会在局域网中搜索核心计算机的位置,并沿着网络中最短的路径感染过去。
​ 而第一轮实验揭露了一个惊人的真相:病毒的不同变种是互斥的。新变种在感染一台已经被旧变种感染的电脑时,会把旧变种完全销毁之后再感染。但研究员发现了实现过程中的漏洞。如果新变种在感染过程中尚未销毁过这类旧变种,需要先花费\(1\)单位时间分析旧变种,才能销毁。如果之前销毁过这类旧变种,就可以认为销毁不花费时间。病毒在两台计算机之间的传播亦可认为不花费时间。
​ 研究员对整个感染过程的耗时特别感兴趣,因为这是消灭病毒的最好时机。于是在\(m\)步实验之中,研究员有时还会做出如下的询问:
\(3\)\(REQUEST\) \(x\)
​ 询问如果在编号为\(x\)的计算机的关键集合中的计算机中植入一个新变种,平均感染时间为多长。编号为\(y\)的计算机在编号为\(x\)的计算机的关键集合中,当且仅当从\(y\)沿网络中的最短路径感染到核心计算机必须经过\(x\)。由于有RECENTER操作的存在,这个集合并不一定是始终不变的。
​ 至此,安全机构认为已经不需要实际的实验了,于是他们拜托你编写一个程序,模拟实验的结果,并回答所有的询问。

​ 简化题面:一棵树,初始根节点为1。每个节点各有一个互不相同的颜色。定义一个结点的权值为该结点到根的颜色种数。支持三个操作:

​ 1.把一个点到根上的路径全部换成一种新颜色。

​ 2.先进行一次1操作,然后再换树根。

​ 3.查询一棵子树内权值的平均值。

Input:

​ 输入的第一行包含两个整数\(n\)\(m\),分别代表局域网中计算机的数量,以及操作和询问的总数。
​ 接下来\(n-1\)行,每行包含两个整数\(x\)\(y\),表示局域网中编号为\(x\)\(y\)的计算机之间有网线直接相连。
​ 接下来\(m\)行,每行包含一个操作或者询问,格式如问题描述中所述。

Output:

​ 对于每个询问,输出一个实数,代表平均感染时间。输出与答案的绝对误差不超过 \(10\) ^ \(-6\)时才会被视为正确。

Sample Input:

8 6
1 2
1 3
2 8
3 4
3 5
3 6
4 7
REQUEST 7
RELEASE 3
REQUEST 3
RECENTER 5
RELEASE 2
REQUEST 1

Sample Output:

4.0000000000
2.0000000000
1.3333333333

Hint:

\(N \leq 100000\)\(M \leq 1 00 000\)

题目分析:

​ 首先观察操作1,由于每次修改的是一条从根到某个节点的链,因此每种颜色对应的结点也就是一条链。那么我们可以用LCT来维护每一种颜色对应的链,那么每次操作1就相当于是一个access操作,操作2就相当于是一个makeroot操作。我们先对原树DFS一边求出DFS序,这样我们就能用线段树维护一个子树内的信息。操作3就变成了线段树查询区间(当然是支持换根的,所以还要求LCA分类讨论,换根DFS序的话可以看看其他经典的板子题)。

​ 然后我们仔细分析操作1,我们access每次找到一种颜色它对应的链深度最小处,把它原来的右儿子的子树内的权值全部+1(因为原先的右儿子内的子树相当于多了一种新颜色),再把现在的右儿子的子树内的权值全部-1(现在的右儿子内的子树相当于把原先不同的颜色变成了同一种新颜色),那么我们就能维护每个结点的权值了。(PS:修改时都是换根DFS序的基础上修改的)

​ 代码如下(马蜂很丑,不喜勿喷)——

#include<bits/stdc++.h>
#define maxn 100005
#define LL long long
using namespace std;
int n,m,tot,rt,st[maxn],tag[maxn],ff[maxn][20],fir[maxn],dfn[maxn],low[maxn],dep[maxn],nxt[maxn<<1],to[maxn<<1],f[maxn],son[maxn][2];LL sum[maxn<<2],add[maxn<<2];
inline void add_eg(int x,int y){to[++tot]=y,nxt[tot]=fir[x],fir[x]=tot;}
inline void pushdown(int x,int l,int r){
	int mid=l+r>>1;add[x<<1]+=add[x],add[x<<1|1]+=add[x];
	sum[x<<1]+=1ll*add[x]*(mid-l+1),sum[x<<1|1]+=1ll*add[x]*(r-mid);add[x]=0;
}
inline void modify(int now,int l,int r,int ll,int rr,int x){
	if(l>=ll&&r<=rr){add[now]+=x,sum[now]+=1ll*x*(r-l+1);return;}pushdown(now,l,r);
	int mid=l+r>>1;if(mid>=ll) modify(now<<1,l,mid,ll,rr,x);if(mid<rr) modify(now<<1|1,mid+1,r,ll,rr,x);sum[now]=sum[now<<1]+sum[now<<1|1];
}
inline LL query(int now,int l,int r,int ll,int rr){
	if(l>=ll&&r<=rr) return sum[now];LL res=0;int mid=l+r>>1;pushdown(now,l,r);
	if(mid>=ll) res=query(now<<1,l,mid,ll,rr);if(mid<rr) res+=query(now<<1|1,mid+1,r,ll,rr);return res;
}
inline void dfs(int x){
	dep[x]=dep[ff[x][0]]+1;for(register int i=1;i<=17;i++) ff[x][i]=ff[ff[x][i-1]][i-1];
	dfn[x]=low[x]=++tot;for(register int i=fir[x];i;i=nxt[i]) if(to[i]!=ff[x][0]) f[to[i]]=ff[to[i]][0]=x,dfs(to[i]),low[x]=max(low[x],low[to[i]]);modify(1,1,n,dfn[x],low[x],1);
}
inline int jump(int x,int y){for(register int i=17;i>=0;i--) if(dep[ff[x][i]]>dep[y]) x=ff[x][i];return x;}
inline bool inside(int y,int x){return dfn[y]>=dfn[x]&&dfn[y]<=low[x];}
inline bool nroot(int x){return son[f[x]][0]==x||son[f[x]][1]==x;}
inline bool get(int x){return son[f[x]][1]==x;}
inline void rev(int x){tag[x]^=1;swap(son[x][0],son[x][1]);}
inline void push(int x){if(!tag[x]) return;if(son[x][0]) rev(son[x][0]);if(son[x][1]) rev(son[x][1]);tag[x]=0;}
inline void rotate(int x){
	int fa=f[x],ffa=f[fa],which=get(x);if(nroot(fa)) son[ffa][son[ffa][1]==fa]=x;
	son[fa][which]=son[x][which^1],son[x][which^1]=fa;f[fa]=x,f[x]=ffa,f[son[fa][which]]=fa;
}
inline void splay(int x){int top=0,y=x;st[++top]=y;while(nroot(y)) y=f[y],st[++top]=y;while(top) push(st[top--]);while(nroot(x)){int fa=f[x];if(nroot(fa)) rotate(get(fa)==get(x)?fa:x);rotate(x);}}
inline int get_rt(int x){while(son[x][0]) push(x),x=son[x][0];return x;}
inline void change(int x,int v){
	if(x==rt){modify(1,1,n,1,n,v);return;}if(!inside(rt,x)){modify(1,1,n,dfn[x],low[x],v);return;}
	x=jump(rt,x);modify(1,1,n,1,n,v),modify(1,1,n,dfn[x],low[x],-v);
}
inline double query_sum(int x){
	if(x==rt) return 1.0*query(1,1,n,1,n)/n;if(!inside(rt,x)) return 1.0*query(1,1,n,dfn[x],low[x])/(low[x]-dfn[x]+1);
	x=jump(rt,x);return 1.0*(query(1,1,n,1,n)-query(1,1,n,dfn[x],low[x]))/(n-low[x]+dfn[x]-1);
}
inline void access(int x){
	int rtt;for(register int y=0;x;x=f[y=x]){
		splay(x);if(son[x][1]){push(x);rtt=get_rt(son[x][1]);change(rtt,1);}
		son[x][1]=y;if(y){push(x);rtt=get_rt(son[x][1]);change(rtt,-1);}
	}
}
inline void makeroot(int x){access(x),splay(x),rev(x);}
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
	return ret*f;
}
int main(){
	n=read(),m=read();for(register int i=1,x,y;i<n;i++) x=read(),y=read(),add_eg(x,y),add_eg(y,x);
	tot=0,dfs(1);rt=1;for(register int i=1;i<=m;i++){
		char ch=getchar();while(ch!='R') ch=getchar();ch=getchar();ch=getchar();int x=read();if(ch=='C'){makeroot(x),rt=x;continue;}
		if(ch=='L'){access(x);continue;}printf("%0.10lf\n",query_sum(x));
	}
	return 0;
}
posted @ 2020-12-27 15:23  OdtreePrince  阅读(82)  评论(0编辑  收藏  举报