1. 程式人生 > >【bzoj 3779】重組病毒

【bzoj 3779】重組病毒

在操作 答案 opened 不同 family req preview edge -1

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

調了幾個世紀終於調出來了……LCT+dfs序線段樹。

令每個點權值為這個點到根的路徑上虛邊數+1,一開始時都是虛邊,點權即為深度。

操作1可以神轉換為access。在access過程中,當前節點的右兒子所代表的子樹整體權值+1(因為虛邊+1),而即將拼接過來的子樹整體權值-1。

操作2因為有換根操作,所以需要分類討論一波(以下結論畫圖易得):

1. x=root,查詢整棵樹;

2. root不在x的子樹內,查詢原樹中x的子樹;

3. rootx的子樹內,查詢整棵樹去除掉包含root的以x的親兒子為根的子樹。

然後就可以開始瞎搞啦?

(順便吐槽一句,結構體好醜啊T_T本來以為寫結構體會快一些的,其實……仿佛差不多

技術分享
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int N=100010;
  6 int n,m,x,y,cnt,dfsn,rt=1;//註意初始化rt為1 
  7 int head[N],st[N];
  8 struct edge{int u,v,next;}e[N*2];//鄰接表 
  9 struct leaf{int l,r;long long sum,tag;}t[N*4];//線段樹 
 10 struct node{int p,fa,c[2],deep,in,out;bool rev;}tr[N];//splay
 11 int read()
 12 {
 13     int x=0,f=1;char c=getchar();
 14     while(c<0||c>9){if(c==-)f=-1;c=getchar();}
 15     while(c>=0&&c<=9){x=x*10+c-0;c=getchar();}
 16     return x*f;
 17 }
 18 void ins(int a,int b){cnt++;e[cnt].u=a;e[cnt].v=b;e[cnt].next=head[a];head[a]=cnt;}
 19 void insert(int a,int b){ins(a,b);ins(b,a);}
 20 void qadd(int num,long long w)
 21 {
 22     t[num].tag+=w;
 23     t[num].sum+=(long long)(t[num].r-t[num].l+1)*w;
 24 }
 25 void pushup(int num){t[num].sum=t[num<<1].sum+t[num<<1|1].sum;}
 26 void pushdown(int num)//線段樹下傳tag 
 27 {
 28     if(!t[num].tag)return;
 29     qadd(num<<1,t[num].tag);
 30     qadd(num<<1|1,t[num].tag);
 31     t[num].tag=0;
 32 }
 33 bool isroot(int k){return !k||(tr[tr[k].fa].c[0]!=k&&tr[tr[k].fa].c[1]!=k);}
 34 void down(int k)//下傳翻轉標記 ,註意不要跟pushdown弄混 
 35 {
 36     if(tr[k].rev)
 37     {
 38         int l=tr[k].c[0],r=tr[k].c[1];
 39         tr[k].rev^=1;tr[l].rev^=1;tr[r].rev^=1;
 40         swap(tr[k].c[0],tr[k].c[1]);
 41     }
 42 }
 43 void rotate(int x)
 44 {
 45     int y=tr[x].fa,z=tr[y].fa,l,r;
 46     if(tr[y].c[0]==x)l=0;else l=1;r=l^1;
 47     if(!isroot(y)){if(tr[z].c[0]==y)tr[z].c[0]=x;else tr[z].c[1]=x;}
 48     tr[x].fa=z;tr[y].fa=x;tr[tr[x].c[r]].fa=y;
 49     tr[y].c[l]=tr[x].c[r];tr[x].c[r]=y;
 50 }
 51 void splay(int x)
 52 {
 53     int top=0;st[++top]=x;
 54     for(int i=x;!isroot(i);i=tr[i].fa)st[++top]=tr[i].fa;
 55     for(int i=top;i;i--)down(st[i]);
 56     while(!isroot(x))
 57     {
 58         int y=tr[x].fa,z=tr[y].fa;
 59         if(!isroot(y))
 60         {
 61             if((tr[y].c[0]==x)^(tr[z].c[0]==y))rotate(x);
 62             else rotate(y);
 63         }
 64         rotate(x);
 65     }
 66 }
 67 void build(int num,int l,int r)
 68 {
 69     t[num].l=l;t[num].r=r;
 70     if(l==r)return;
 71     int mid=(l+r)>>1;
 72     build(num<<1,l,mid);build(num<<1|1,mid+1,r);
 73 }
 74 void add(int num,int l,int r,long long w)//線段樹區間加 
 75 {
 76     if(l>r)return;
 77     if(l<=t[num].l&&t[num].r<=r){qadd(num,w);return;}
 78     if(t[num].l==t[num].r)return;
 79     pushdown(num);
 80     int mid=(t[num].l+t[num].r)>>1;
 81     if(l<=mid)add(num<<1,l,r,w);
 82     if(r>mid)add(num<<1|1,l,r,w);
 83     pushup(num);
 84 }
 85 void dfs(int now,int last)
 86 {
 87     tr[now].p=tr[now].fa=last;
 88     tr[now].in=++dfsn;
 89     tr[now].deep=tr[last].deep+1;
 90     add(1,tr[now].in,tr[now].in,tr[now].deep);//在線段樹中更新 
 91     for(int i=head[now];i;i=e[i].next)
 92         if(e[i].v!=last)dfs(e[i].v,now);//記得判父節點 
 93     tr[now].out=dfsn;
 94 }
 95 long long query(int num,int l,int r)//線段樹區間求和 
 96 {
 97     if(l>r)return 0;
 98     if(l<=t[num].l&&t[num].r<=r)return t[num].sum;
 99     pushdown(num);//記得下傳tag 
100     long long ans=0;
101     int mid=(t[num].l+t[num].r)>>1;
102     if(l<=mid)ans+=query(num<<1,l,r);
103     if(r>mid)ans+=query(num<<1|1,l,r);
104     return ans;
105 }
106 int find(int num,int goal)//查找goal在num的哪一棵子樹內 
107 {
108     for(int i=head[num];i;i=e[i].next)
109         if(e[i].v!=tr[num].p&&tr[goal].in>=tr[e[i].v].in&&tr[goal].out<=tr[e[i].v].out)return e[i].v;
110     return 0;
111 }
112 double ask(int num)//REQUEST
113 {
114     if(num==rt)return (double)query(1,1,n)/n;
115     if(tr[rt].in>=tr[num].in&&tr[rt].out<=tr[num].out)//如果rt在num的子樹內;這裏的子樹是相對於原樹而言 
116     {
117         int r=find(num,rt);
118         return (double)(query(1,1,tr[r].in-1)+query(1,tr[r].out+1,n))/(n-(tr[r].out-tr[r].in+1));
119     }
120     return (double)query(1,tr[num].in,tr[num].out)/(tr[num].out-tr[num].in+1);
121 }
122 int top(int num)
123 {
124     down(num);
125     while(tr[num].c[0])num=tr[num].c[0],down(num);
126     return num;
127 }
128 void ladd(int num,int w)//子樹整體權值加減 
129 {
130     if(num==rt)add(1,1,n,w);
131     else if(tr[rt].in>=tr[num].in&&tr[rt].out<=tr[num].out)
132     {
133         int r=find(num,rt);
134         add(1,1,tr[r].in-1,w);
135         add(1,tr[r].out+1,n,w);
136     }
137     else add(1,tr[num].in,tr[num].out,w);
138 }
139 void acs(int num)
140 {
141     int r=0;
142     while(num)
143     {
144         splay(num);
145         if(tr[num].c[1])ladd(top(tr[num].c[1]),1);
146         if(r)ladd(top(r),-1);
147         tr[num].c[1]=r;r=num;num=tr[num].fa;
148     }
149 }
150 void mrt(int num){splay(num);rt=num;tr[num].rev^=1;}//據題意,換根前已經access過了 
151 int main()
152 {
153     char ch[15];
154     n=read();m=read();
155     for(int i=1;i<n;i++)x=read(),y=read(),insert(x,y);
156     build(1,1,n);dfs(1,0);
157     while(m--)
158     {
159         scanf("%s",ch);x=read();
160         if(ch[2]==Q)printf("%.10lf\n",ask(x));
161         else
162         {
163             acs(x);
164             if(ch[2]==C)mrt(x);
165         }
166     }
167     return 0;
168 }
View Code

【bzoj 3779】重組病毒