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操作的存在,这个集合并不一定是始终不变的。
至此,安全机构认为已经不需要实际的实验了,于是他们拜托你编写一个程序,模拟实验的结果,并回答所有的询问。
实验在一个封闭的局域网内进行。局域网内有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操作的存在,这个集合并不一定是始终不变的。
至此,安全机构认为已经不需要实际的实验了,于是他们拜托你编写一个程序,模拟实验的结果,并回答所有的询问。
Input
输入的第一行包含两个整数n和m,分别代表局域网中计算机的数量,以及操作和询问的总数。
接下来n-1行,每行包含两个整数x和y,表示局域网中编号为x和y的计算机之间有网线直接相连。
接下来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
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
2.0000000000
1.3333333333
HINT
N < = 1 00 000 M < = 1 00 000
思路{
首先发现前两个操作的过程就相当于$LCT$中的$access$和$makeroot$操作。
然后询问实质上就变成了询问到当前总根的每个$Splay$之间虚边的个数$+1$。
用$LCT$模拟这个过程,然后在$access$的时候动态维护点的贡献。
具体来说就是虚边->实边,$Ans--$;实边->虚边,$Ans++$。
下放$rev$标记的时间要注意一下。
由于还需要维护子树的信息,可以用树状数组维护$DFS$序即可。
然后大力分类讨论。
}
#include<bits/stdc++.h> #define db double #define ll long long #define RG register #define il inline #define N 100010 using namespace std; int n; struct ed{int nxt,to;}e[N*2]; int head[N],tot,dep[N],id,f[18][N]; int ch[N][2],fa[N],dfn[N],til[N],st[N],rt;bool rev[N]; namespace BIT{ #define lowbit ((x)&(-x)) ll T1[N],T2[N]; void modify(int x,int delta){int xx=x; for(;x<=n;x+=lowbit)T1[x]+=delta,T2[x]+=1ll*xx*delta; } void Modify(int l,int r,int delta){modify(l,delta),modify(r+1,-delta);} ll query(int x){ ll sum(0);int xx=x; for(;x;x-=lowbit)sum+=(xx+1)*T1[x]-T2[x]; return sum; } ll Query(int l,int r){return query(r)-query(l-1);} } #define rs (ch[x][1]) #define ls (ch[x][0]) void down(int x){ if(rev[x]){ rev[x]^=1,rev[ls]^=1;rev[rs]^=1; swap(rs,ls); } } bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} void Rotate(int x){ int y=fa[x],z=fa[y]; int l=(ch[y][1]==x),r=l^1; if(!isroot(y))ch[z][ch[z][1]==y]=x; ch[y][l]=ch[x][r],fa[ch[x][r]]=y,ch[x][r]=y; fa[x]=z,fa[y]=x; } void Splay(int x){ int top(0),y=x,z;st[++top]=y; while(!isroot(y))st[++top]=fa[y],y=fa[y]; for(int i=top;i;i--)down(st[i]); while(!isroot(x)){ y=fa[x],z=fa[y]; if(!isroot(y)){ if(ch[y][0]==x^ch[z][0]==y)Rotate(x); else Rotate(y); }Rotate(x); } } int find(int x){down(x); while(ls)x=ls,down(x); return x; } int jump(int x,int y){ for(int i=17;i!=-1;i--)if(dep[f[i][x]]>dep[y])x=f[i][x]; return x; } void Modify(int x,int delta){ if(dfn[rt]<dfn[x]||dfn[rt]>til[x])BIT::Modify(dfn[x],til[x],delta); else { int u=jump(rt,x); if(dfn[u]!=1)BIT::Modify(1,dfn[u]-1,delta); if(til[u]!=n)BIT::Modify(til[u]+1,n,delta); } } void access(int x){ int t(0); while(x){ Splay(x); if(t){ int u=find(t); Modify(u,-1); } if(ch[x][1]){ int u=find(ch[x][1]); Modify(u,1); } ch[x][1]=t;t=x,x=fa[x]; } } void makeroot(int x){ access(x); Splay(x);rev[x]^=1;rt=x; } void query(int x){ if(x==rt){ db tmp=1.0*(BIT::Query(1,n))/n; printf("%.10lf\n",tmp);return; } if(dfn[rt]<dfn[x]||dfn[rt]>til[x]){ db tmp=1.0*(BIT::Query(dfn[x],til[x]))/(til[x]-dfn[x]+1); printf("%.10lf\n",tmp); } else { int u=jump(rt,x),sz(0);db tmp=0; if(dfn[u]!=1)tmp+=BIT::Query(1,dfn[u]-1),sz+=dfn[u]-1; if(til[u]!=n)tmp+=BIT::Query(til[u]+1,n),sz+=n-til[u]; tmp/=sz;printf("%.10lf\n",tmp); } } void link(int u,int v){e[tot].nxt=head[u];e[tot].to=v;head[u]=tot++;} void dfs(int u,int faa){dfn[u]=++id; fa[u]=faa,dep[u]=dep[faa]+1;BIT::Modify(dfn[u],dfn[u],dep[u]); f[0][u]=faa; for(int i=1;i<=17;++i)f[i][u]=f[i-1][f[i-1][u]]; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to;if(v==faa)continue; dfs(v,u); } til[u]=id; } char s[20]; int main(){ freopen("1.in","r",stdin); freopen("1.out","w",stdout); int Q;scanf("%d%d",&n,&Q); memset(head,-1,sizeof(head)); for(int i=1;i<n;++i){ int u,v;scanf("%d%d",&u,&v); link(u,v),link(v,u); }dfs(1,1);fa[1]=0; for(int _=1;_<=Q;++_){ int x;scanf("%s",s+1);scanf("%d",&x); if(s[3]=='L')access(x); if(s[3]=='C')makeroot(x); if(s[3]=='Q')query(x); }return 0; }