原题干(由于是权限题我就直接砸出原题干了,要看题意概述的话在下面):
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操作的存在,这个集合并不一定是始终不变的。
至此,安全机构认为已经不需要实际的实验了,于是他们拜托你编写一个程序,模拟实验的结果,并回答所有的询问。
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<=100000,M<=100000.
题意概述:给出一棵树,初始每个结点有不同的颜色。现在支持三种操作:1.把某个结点的颜色改成一个之前都没有出现过的颜色,并将这个点到当前树根路径上的所有点全部改成这个颜色;2.改变当前的树根到另外一个点,并对原来的树根进行一次操作1;3.把询问当前形态的树中对一个点的子树中所有点进行操作1的平均代价(操作1代价的计算方式:这个点到当前树根路径上的不同颜色数量)。
从原题干来分析,相同的颜色一定连续出现在一条链上。再仔细想想,这样的操作正是LCT中的access操作!而每一次操作的代价就是access的时候经历的虚边的数量。如果是单点询问的话这就是一道LCT的裸题,但是问题在于题目要求的是子树询问。对子树信息的维护很容易想到用DFS序配合线段树来搞定。(于是就有两种流派:线段树单独在外面维护子树信息和就在LCT中维护子树信息两种,我选择线段树,常数小啊!如果要Link和Cut的话大不了我再来个LCT维护一下DFS序。。。)
分析没有换根操作的情况,在access操作中,令函数调用的时候的对象为s,当前所在结点为x,上一次所在的splay的根结点为y。y所在的splay就是s->y所在splay权值(真实树中深度)最小的点的一条链。我们找到链顶,可以发现这个链顶的子树中所有及结点在这一次access跳跃之后答案都要-1(每个点到根上的路径都少了一种颜色),x在当前树中位于splay维护的链上的儿子的子树中所有结点的答案+1。一路维护上去所有被影响的点的答案都被更新了。
换根的情况?可以发现在没有换根操作的时候可以保证对每个点的子树进行操作的时候一定是对应dfs序中一段连续的序列,但是换根之后就可以出现当前某个点子树在以1为根的dfs序中序列不连续,但是实际上也可以发现就是被分成了两端,x的子树对应的dfs序就是把x当前的父亲从dfs序中挖掉剩下的部分。对于是不是要挖点的判断借助DFS序就可以做到了。
随便分析几下思路就有了,也不是很难码,关键是细节,各种情况考虑全(为了节省您的时间请一定把一切想清楚了再开始码)。。。。。。这里只说一点:注意对LCT每个点的儿子的正确维护!
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 const int MAXN=100005; 14 typedef long long LL; 15 16 int N,M,root_now; 17 struct edge{ int to,next; }E[MAXN<<1]; 18 int first[MAXN],np,val[MAXN],dfs_clock,l[MAXN],r[MAXN]; 19 struct segment_tree{ 20 static const int maxn=200005; 21 int rt,np,lc[maxn],rc[maxn]; LL sum[maxn],add[maxn]; 22 void pushup(int now) { sum[now]=sum[lc[now]]+sum[rc[now]]; } 23 void pushdown(int now,int L,int R) 24 { 25 if(!add[now]) return; 26 int m=L+R>>1; 27 add[lc[now]]+=add[now],sum[lc[now]]+=add[now]*(m-L+1); 28 add[rc[now]]+=add[now],sum[rc[now]]+=add[now]*(R-m); 29 add[now]=0; 30 } 31 void build(int &now,int L,int R,int *a) 32 { 33 now=++np; 34 lc[now]=rc[now]=sum[now]=add[now]=0; 35 if(L==R){ sum[now]=a[L]; return;} 36 int m=L+R>>1; 37 build(lc[now],L,m,a); build(rc[now],m+1,R,a); 38 pushup(now); 39 } 40 void update(int now,int L,int R,int A,int B,int v) 41 { 42 if(A<=L&&R<=B){ 43 add[now]+=v,sum[now]+=(R-L+1)*v; 44 return; 45 } 46 pushdown(now,L,R); 47 int m=L+R>>1; 48 if(B<=m) update(lc[now],L,m,A,B,v); 49 else if(A>m) update(rc[now],m+1,R,A,B,v); 50 else update(lc[now],L,m,A,B,v),update(rc[now],m+1,R,A,B,v); 51 pushup(now); 52 } 53 LL query(int now,int L,int R,int A,int B) 54 { 55 if(A<=L&&R<=B) return sum[now]; 56 pushdown(now,L,R); 57 int m=L+R>>1; 58 if(B<=m) return query(lc[now],L,m,A,B); 59 if(A>m) return query(rc[now],m+1,R,A,B); 60 return query(lc[now],L,m,A,B)+query(rc[now],m+1,R,A,B); 61 } 62 }st; 63 struct link_cut_tree{ 64 static const int maxn=100005; 65 struct node{ 66 int fa,ch[2]; bool rev; 67 node(){ fa=ch[0]=ch[2]=0,rev=0; } 68 }nd[maxn]; 69 void link(int x,int d,int y) { nd[x].ch[d]=y,nd[y].fa=x; } 70 bool isrt(int x) { return nd[nd[x].fa].ch[0]!=x&&nd[nd[x].fa].ch[1]!=x; } 71 void pushdown(int x) 72 { 73 if(!nd[x].rev) return; 74 int lc=nd[x].ch[0],rc=nd[x].ch[1]; 75 if(lc) nd[lc].rev^=1,swap(nd[lc].ch[0],nd[lc].ch[1]); 76 if(rc) nd[rc].rev^=1,swap(nd[rc].ch[0],nd[rc].ch[1]); 77 nd[x].rev=0; 78 } 79 void rot(int x) 80 { 81 int y=nd[x].fa,z=nd[y].fa; 82 pushdown(y);pushdown(x); 83 int d=nd[y].ch[0]==x; 84 if(!isrt(y)) link(z,y==nd[z].ch[1],x); nd[x].fa=z; 85 link(y,d^1,nd[x].ch[d]); 86 link(x,d,y); 87 } 88 void splay(int x) 89 { 90 pushdown(x); 91 while(!isrt(x)){ 92 int y=nd[x].fa,z=nd[y].fa; 93 if(!isrt(y)) rot((x==nd[y].ch[0])==(y==nd[z].ch[0])?y:x); 94 rot(x); 95 } 96 } 97 int find(int x,int d) 98 { 99 while(nd[x].ch[d]){ pushdown(x),x=nd[x].ch[d]; } 100 return x; 101 } 102 void access(int x) 103 { 104 int y=0,xx,yy; 105 while(x){ 106 splay(x); 107 if(y){ 108 yy=find(y,0); 109 if(l[x]<l[yy]&&l[yy]<=r[x]) st.update(st.rt,1,N,l[yy],r[yy],-1); 110 else{ 111 if(l[x]>1) st.update(st.rt,1,N,1,l[x]-1,-1); 112 if(r[x]<N) st.update(st.rt,1,N,r[x]+1,N,-1); 113 } 114 } 115 pushdown(x); 116 if((xx=find(nd[x].ch[1],0))&&l[x]<l[xx]&&l[xx]<=r[x]) st.update(st.rt,1,N,l[xx],r[xx],1); 117 else if(xx){ 118 if(l[x]>1) st.update(st.rt,1,N,1,l[x]-1,1); 119 if(r[x]<N) st.update(st.rt,1,N,r[x]+1,N,1); 120 } 121 nd[x].ch[1]=y,y=x,x=nd[x].fa; 122 } 123 } 124 void mroot(int x) 125 { 126 access(x); splay(x); 127 nd[x].rev^=1,swap(nd[x].ch[0],nd[x].ch[1]); 128 } 129 }lct; 130 131 void add_edge(int u,int v) 132 { 133 E[++np]=(edge){v,first[u]}; 134 first[u]=np; 135 } 136 void data_in() 137 { 138 scanf("%d%d",&N,&M); 139 int x,y; 140 for(int i=1;i<N;i++){ 141 scanf("%d%d",&x,&y); 142 add_edge(x,y); add_edge(y,x); 143 } 144 } 145 void DFS(int i,int f,int d) 146 { 147 l[i]=++dfs_clock,val[l[i]]=d; 148 for(int p=first[i];p;p=E[p].next){ 149 int j=E[p].to; 150 if(j==f) continue; 151 lct.nd[j].fa=i; 152 DFS(j,i,d+1); 153 } 154 r[i]=dfs_clock; 155 } 156 void work() 157 { 158 DFS(root_now=1,0,1); 159 st.build(st.rt,1,N,val); 160 char op[10]; int x,len; double ans; 161 for(int i=1;i<=M;i++){ 162 scanf("%s%d",op,&x); 163 if(op[2]==‘L‘) lct.access(x); 164 else if(op[2]==‘C‘) lct.mroot(root_now=x); 165 else if(op[2]==‘Q‘){ 166 ans=0,len=0; 167 if(l[x]<=l[root_now]&&r[root_now]<=r[x]){ 168 lct.splay(x); 169 x=lct.nd[x].ch[0]?lct.find(lct.nd[x].ch[0],1):lct.nd[x].fa; 170 if(l[x]>1) ans+=1.0*st.query(st.rt,1,N,1,l[x]-1),len+=l[x]-1; 171 if(r[x]<N) ans+=1.0*st.query(st.rt,1,N,r[x]+1,N),len+=N-r[x]; 172 ans/=len; 173 } 174 else ans=1.0*st.query(st.rt,1,N,l[x],r[x])/(r[x]-l[x]+1); 175 printf("%.10f\n",ans); 176 } 177 } 178 } 179 int main() 180 { 181 freopen("test.in","r",stdin); 182 freopen("test.out","w",stdout); 183 data_in(); 184 work(); 185 return 0; 186 }
原文地址:https://www.cnblogs.com/KKKorange/p/8494283.html