BZOJ3779重組病毒LCT
題目描述
黑客們通過對已有的病毒反編譯,將許多不同的病毒重組,並重新編譯出了新型的重組病毒。這種病毒的繁殖和變異能力極強。為了阻止這種病毒傳播,某安全機構策劃了一次實驗,來研究這種病毒。
實驗在一個封閉的區域網內進行。區域網內有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、將當前點到根染上一種新的顏色,這意味著這條路徑上的點到其他點顏色是不一樣的。
2、對x點進行操作1,然後把x作為根。
我們發現這兩個操作和LCT中的access和makeroot一毛一樣,重邊代表兩點顏色相同,輕邊代表顏色不同。
然後就用LCT維護這個過程。
但要求區間查詢,所以再加上線段樹維護dfs序。
access時,如果斷開一條重邊,那麼子樹裡答案要+1,如果連上一條重邊,那麼子樹-1。
然後換根的話和遙遠的國度那題一樣,討論一下當前點和根的關係就好了。
注意:線段樹區間加別寫錯,子樹加的時候要先findroot一下找到真正的子樹根。
程式碼
#include<iostream> #include<cstdio> #define N 100002 using namespace std; typedef long long ll; char s[10]; ll tr[N<<2],la[N<<2]; int ch[N][2],fa[N],dfn[N],size[N],root,n,m,tot,top,head[N],deep[N]; int p[N][20]; bool rev[N]; inline int rd(){ int x=0;char c=getchar();boolf=0; while(!isdigit(c)){if(c=='-')f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f?-x:x; } inline void pushd(int cnt,int l1,int l2){ tr[cnt<<1]+=la[cnt]*l1;la[cnt<<1]+=la[cnt]; tr[cnt<<1|1]+=la[cnt]*l2;la[cnt<<1|1]+=la[cnt]; la[cnt]=0; } void upd(int cnt,int l,int r,int L,int R,int x){ if(L>R)return; if(l>=L&&r<=R){tr[cnt]+=1ll*(r-l+1)*x;la[cnt]+=x;return;} int mid=(l+r)>>1; if(la[cnt])pushd(cnt,mid-l+1,r-mid); if(mid>=L)upd(cnt<<1,l,mid,L,R,x); if(mid<R)upd(cnt<<1|1,mid+1,r,L,R,x); tr[cnt]=tr[cnt<<1]+tr[cnt<<1|1]; } ll query(int cnt,int l,int r,int L,int R){ if(L>R)return 0; if(l>=L&&r<=R)return tr[cnt]; int mid=(l+r)>>1;ll ans=0; if(la[cnt])pushd(cnt,mid-l+1,r-mid); if(mid>=L)ans+=query(cnt<<1,l,mid,L,R); if(mid<R)ans+=query(cnt<<1|1,mid+1,r,L,R); return ans; } #define ls ch[x][0] #define rs ch[x][1] inline bool ge(int x){return ch[fa[x]][1]==x;} inline bool isroot(int x){return ch[fa[x]][1]!=x&&ch[fa[x]][0]!=x;} inline void rotate(int x){ int y=fa[x],o=ge(x); ch[y][o]=ch[x][o^1];fa[ch[y][o]]=y; if(!isroot(y))ch[fa[y]][ge(y)]=x;fa[x]=fa[y]; fa[y]=x;ch[x][o^1]=y; } inline void pushdown(int x){if(rev[x]){rev[x]^=1;rev[ls]^=1;rev[rs]^=1;swap(ls,rs);}} inline void _pushdown(int x){if(!isroot(x))_pushdown(fa[x]);pushdown(x);} inline void splay(int x){ _pushdown(x); while(!isroot(x)){ int y=fa[x]; if(isroot(y))rotate(x); else rotate(ge(x)==ge(y)?y:x),rotate(x); } } inline int jump(int x,int y){ if(y<=0)return x; for(int i=17;i>=0;--i)if((1<<i)&y)x=p[x][i]; return x; } inline void jia(int x,int tag){ if(root==x)upd(1,1,n,1,n,tag); else if(dfn[root]>=dfn[x]&&dfn[root]<=dfn[x]+size[x]-1){ int y=jump(root,deep[root]-deep[x]-1); upd(1,1,n,1,dfn[y]-1,tag);upd(1,1,n,dfn[y]+size[y],n,tag); } else upd(1,1,n,dfn[x],dfn[x]+size[x]-1,tag); } inline int findroot(int x){ pushdown(x); while(ch[x][0])x=ch[x][0],pushdown(x); return x; } inline void access(int x){ for(int y=0;x;y=x,x=fa[x]){ splay(x); if(ch[x][1])jia(findroot(ch[x][1]),1);if(y)jia(findroot(y),-1); ch[x][1]=y; } } inline void makeroot(int x){access(x);splay(x);rev[x]^=1;} struct edge{int n,to;}e[N<<1]; inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;} void dfs(int u,int f){ dfn[u]=++top;size[u]=1;deep[u]=deep[f]+1; for(int i=1;(1<<i)<=deep[u];++i)p[u][i]=p[p[u][i-1]][i-1]; upd(1,1,n,dfn[u],dfn[u],deep[u]); for(int i=head[u];i;i=e[i].n)if(e[i].to!=f){ int v=e[i].to;fa[v]=u;p[v][0]=u; dfs(v,u);size[u]+=size[v]; } } int main(){ n=rd();m=rd();int x,y; for(int i=1;i<n;++i){x=rd();y=rd();add(x,y);add(y,x);} dfs(1,0);root=1; for(int i=1;i<=m;++i){ scanf("%s",s);x=rd(); if(s[2]=='L')access(x); else if(s[2]=='C')makeroot(x),root=x; else{ double sum=0; if(root==x)sum=(double)query(1,1,n,1,n)/n; else if(dfn[root]>=dfn[x]&&dfn[root]<=dfn[x]+size[x]-1){ int y=jump(root,deep[root]-deep[x]-1); sum=(double)(query(1,1,n,1,dfn[y]-1)+query(1,1,n,dfn[y]+size[y],n))/(n-size[y]); } else sum=(double)query(1,1,n,dfn[x],dfn[x]+size[x]-1)/size[x]; printf("%.10lf\n",sum); } } return 0; }