【bzoj 3779】重组病毒
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
Sample Output
4.0000000000
2.0000000000
1.3333333333
HINT
N<=100000 ,M<=100000
调了几个世纪终于调出来了……LCT+dfs序线段树。
令每个点权值为这个点到根的路径上虚边数+1,一开始时都是虚边,点权即为深度。
操作1可以神转换为access。在access过程中,当前节点的右儿子所代表的子树整体权值+1(因为虚边+1),而即将拼接过来的子树整体权值-1。
操作2因为有换根操作,所以需要分类讨论一波(以下结论画图易得):
1. x=root,查询整棵树;
2. root不在x的子树内,查询原树中x的子树;
3. root在x的子树内,查询整棵树去除掉包含root的以x的亲儿子为根的子树。
然后就可以开始瞎搞啦✔
Update:原来的代码有一点问题……重新写了一份。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define LL long long 5 #define lc(x) x<<1 6 #define rc(x) x<<1|1 7 using namespace std; 8 const int N=1e5+5; 9 int n,m,x,y,cnt,dfn,rt=1,first[N]; 10 int deep[N],in[N],out[N],fa[N][18]; 11 char op[15]; 12 int read() 13 { 14 int x=0,f=1;char c=getchar(); 15 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 16 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 17 return x*f; 18 } 19 struct edge{int to,next;}e[N<<1]; 20 void ins(int u,int v){e[++cnt]=(edge){v,first[u]};first[u]=cnt;} 21 struct SGT 22 { 23 int l[N*4],r[N*4]; 24 LL sum[N*4],tag[N*4]; 25 void build(int x,int L,int R) 26 { 27 l[x]=L;r[x]=R;if(L==R)return; 28 int mid=(L+R)>>1; 29 build(lc(x),L,mid);build(rc(x),mid+1,R); 30 } 31 void up(int x){sum[x]=sum[lc(x)]+sum[rc(x)];} 32 void qadd(int x,LL w){tag[x]+=w;sum[x]+=w*(r[x]-l[x]+1);} 33 void down(int x) 34 { 35 if(!tag[x])return; 36 qadd(lc(x),tag[x]);qadd(rc(x),tag[x]); 37 tag[x]=0; 38 } 39 void add(int x,int L,int R,LL w) 40 { 41 if(L<=l[x]&&r[x]<=R){qadd(x,w);return;} 42 down(x);int mid=(l[x]+r[x])>>1; 43 if(L<=mid)add(lc(x),L,R,w); 44 if(R>mid)add(rc(x),L,R,w); 45 up(x); 46 } 47 LL query(int x,int L,int R) 48 { 49 if(L<=l[x]&&r[x]<=R)return sum[x]; 50 down(x);LL ans=0; 51 int mid=(l[x]+r[x])>>1; 52 if(L<=mid)ans+=query(lc(x),L,R); 53 if(R>mid)ans+=query(rc(x),L,R); 54 return ans; 55 } 56 }sgt; 57 int find(int x,int y) 58 { 59 int d=deep[y]-deep[x]-1; 60 for(int i=16;i>=0;i--) 61 if(d&(1<<i))y=fa[y][i]; 62 return y; 63 } 64 void addson(int x,LL w) 65 { 66 if(x==rt)sgt.add(1,1,n,w); 67 else if(in[rt]>=in[x]&&out[rt]<=out[x]) 68 { 69 int t=find(x,rt); 70 if(in[t]>1)sgt.add(1,1,in[t]-1,w); 71 if(out[t]<n)sgt.add(1,out[t]+1,n,w); 72 } 73 else sgt.add(1,in[x],out[x],w); 74 } 75 struct LCT 76 { 77 int c[N][2],fa[N],in[N],out[N]; 78 bool rev[N]; 79 bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;} 80 void flip(int x){swap(c[x][0],c[x][1]);rev[x]^=1;} 81 void down(int x){if(rev[x])flip(c[x][0]),flip(c[x][1]),rev[x]=0;} 82 void rotate(int x) 83 { 84 int y=fa[x],z=fa[y],l,r; 85 if(c[y][0]==x)l=0;else l=1;r=l^1; 86 if(!isroot(y)){if(c[z][0]==y)c[z][0]=x;else c[z][1]=x;} 87 fa[x]=z;fa[y]=x;fa[c[x][r]]=y; 88 c[y][l]=c[x][r];c[x][r]=y; 89 } 90 void relax(int x){if(!isroot(x))relax(fa[x]);down(x);} 91 void splay(int x) 92 { 93 relax(x); 94 while(!isroot(x)) 95 { 96 int y=fa[x],z=fa[y]; 97 if(!isroot(y)) 98 { 99 if((c[y][0]==x)^(c[z][0]==y))rotate(x); 100 else rotate(y); 101 } 102 rotate(x); 103 } 104 } 105 int top(int x){down(x);while(c[x][0])x=c[x][0],down(x);return x;} 106 void access(int x) 107 { 108 int t=0; 109 while(x) 110 { 111 splay(x); 112 if(c[x][1])addson(top(c[x][1]),1); 113 if(t)addson(top(t),-1); 114 c[x][1]=t;t=x;x=fa[x]; 115 } 116 } 117 void makeroot(int x){splay(x);rt=x;flip(x);} 118 }lct; 119 void dfs(int x) 120 { 121 in[x]=++dfn; 122 sgt.add(1,in[x],in[x],deep[x]); 123 for(int i=1;(1<<i)<=deep[x];i++) 124 fa[x][i]=fa[fa[x][i-1]][i-1]; 125 for(int i=first[x];i;i=e[i].next) 126 { 127 int to=e[i].to; 128 if(to==fa[x][0])continue; 129 fa[to][0]=lct.fa[to]=x; 130 deep[to]=deep[x]+1;dfs(to); 131 } 132 out[x]=dfn; 133 } 134 double request(int x) 135 { 136 if(x==rt)return 1.0*sgt.query(1,1,n)/n; 137 if(in[rt]>=in[x]&&out[rt]<=out[x]) 138 { 139 int t=find(x,rt);LL sum=0; 140 if(in[t]>1)sum+=sgt.query(1,1,in[t]-1); 141 if(out[t]<n)sum+=sgt.query(1,out[t]+1,n); 142 return 1.0*sum/(n-(out[t]-in[t]+1)); 143 } 144 return 1.0*sgt.query(1,in[x],out[x])/(out[x]-in[x]+1); 145 } 146 int main() 147 { 148 n=read();m=read(); 149 for(int i=1;i<n;i++)x=read(),y=read(),ins(x,y),ins(y,x); 150 sgt.build(1,1,n);deep[1]=1;dfs(1); 151 while(m--) 152 { 153 scanf("%s",op);x=read(); 154 if(op[2]=='Q')printf("%.10lf\n",request(x)); 155 else 156 { 157 lct.access(x); 158 if(op[2]=='C')lct.makeroot(x); 159 } 160 } 161 return 0; 162 }