BZOJ 3779 重組病毒 LCT+線段樹(維護DFS序)

原題幹(由於是權限題我就直接砸出原題幹了,要看題意概述的話在下面):

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每個點的兒子的正確維護!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
const int MAXN=100005;
typedef long long LL;

int N,M,root_now;
struct edge{ int to,next; }E[MAXN<<1];
int first[MAXN],np,val[MAXN],dfs_clock,l[MAXN],r[MAXN];
struct segment_tree{
    static const int maxn=200005;
    int rt,np,lc[maxn],rc[maxn]; LL sum[maxn],add[maxn];
    void pushup(int now) { sum[now]=sum[lc[now]]+sum[rc[now]]; }
    void pushdown(int now,int L,int R)
    {
        if(!add[now]) return;
        int m=L+R>>1;
        add[lc[now]]+=add[now],sum[lc[now]]+=add[now]*(m-L+1);
        add[rc[now]]+=add[now],sum[rc[now]]+=add[now]*(R-m);
        add[now]=0;
    }
    void build(int &now,int L,int R,int *a)
    {
        now=++np;
        lc[now]=rc[now]=sum[now]=add[now]=0;
        if(L==R){ sum[now]=a[L]; return;}
        int m=L+R>>1;
        build(lc[now],L,m,a); build(rc[now],m+1,R,a);
        pushup(now);
    }
    void update(int now,int L,int R,int A,int B,int v)
    {
        if(A<=L&&R<=B){
            add[now]+=v,sum[now]+=(R-L+1)*v;
            return;
        }
        pushdown(now,L,R);
        int m=L+R>>1;
        if(B<=m) update(lc[now],L,m,A,B,v);
        else if(A>m) update(rc[now],m+1,R,A,B,v);
        else update(lc[now],L,m,A,B,v),update(rc[now],m+1,R,A,B,v);
        pushup(now);
    }
    LL query(int now,int L,int R,int A,int B)
    {
        if(A<=L&&R<=B) return sum[now];
        pushdown(now,L,R);
        int m=L+R>>1;
        if(B<=m) return query(lc[now],L,m,A,B);
        if(A>m) return query(rc[now],m+1,R,A,B);
        return query(lc[now],L,m,A,B)+query(rc[now],m+1,R,A,B);
    }
}st;
struct link_cut_tree{
    static const int maxn=100005;
    struct node{
        int fa,ch[2]; bool rev;
        node(){ fa=ch[0]=ch[2]=0,rev=0; }
    }nd[maxn];
    void link(int x,int d,int y) { nd[x].ch[d]=y,nd[y].fa=x; }
    bool isrt(int x) { return nd[nd[x].fa].ch[0]!=x&&nd[nd[x].fa].ch[1]!=x; }
    void pushdown(int x)
    {
        if(!nd[x].rev) return;
        int lc=nd[x].ch[0],rc=nd[x].ch[1];
        if(lc) nd[lc].rev^=1,swap(nd[lc].ch[0],nd[lc].ch[1]);
        if(rc) nd[rc].rev^=1,swap(nd[rc].ch[0],nd[rc].ch[1]);
        nd[x].rev=0;
    }
    void rot(int x)
    {
        int y=nd[x].fa,z=nd[y].fa;
        pushdown(y);pushdown(x);
        int d=nd[y].ch[0]==x;
        if(!isrt(y)) link(z,y==nd[z].ch[1],x); nd[x].fa=z;
        link(y,d^1,nd[x].ch[d]);
        link(x,d,y);
    }
    void splay(int x)
    {
        pushdown(x);
        while(!isrt(x)){
            int y=nd[x].fa,z=nd[y].fa;
            if(!isrt(y)) rot((x==nd[y].ch[0])==(y==nd[z].ch[0])?y:x);
            rot(x);
        }
    }
    int find(int x,int d)
    {
        while(nd[x].ch[d]){ pushdown(x),x=nd[x].ch[d]; }
        return x;
    }
    void access(int x)
    {
        int y=0,xx,yy;
        while(x){
            splay(x);
            if(y){
                yy=find(y,0);
                if(l[x]<l[yy]&&l[yy]<=r[x]) st.update(st.rt,1,N,l[yy],r[yy],-1);
                else{
                    if(l[x]>1) st.update(st.rt,1,N,1,l[x]-1,-1);
                    if(r[x]<N) st.update(st.rt,1,N,r[x]+1,N,-1);
                }
            }
            pushdown(x);
            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);
            else if(xx){
                if(l[x]>1) st.update(st.rt,1,N,1,l[x]-1,1);
                if(r[x]<N) st.update(st.rt,1,N,r[x]+1,N,1);
            }
            nd[x].ch[1]=y,y=x,x=nd[x].fa;
        }
    }
    void mroot(int x)
    {
        access(x); splay(x);
        nd[x].rev^=1,swap(nd[x].ch[0],nd[x].ch[1]);
    }
}lct;

void add_edge(int u,int v)
{
    E[++np]=(edge){v,first[u]};
    first[u]=np;
}
void data_in()
{
    scanf("%d%d",&N,&M);
    int x,y;
    for(int i=1;i<N;i++){
        scanf("%d%d",&x,&y);
        add_edge(x,y); add_edge(y,x);
    }
}
void DFS(int i,int f,int d)
{
    l[i]=++dfs_clock,val[l[i]]=d;
    for(int p=first[i];p;p=E[p].next){
        int j=E[p].to;
        if(j==f) continue;
        lct.nd[j].fa=i;
        DFS(j,i,d+1);
    }
    r[i]=dfs_clock;
}
void work()
{
    DFS(root_now=1,0,1);
    st.build(st.rt,1,N,val);
    char op[10]; int x,len; double ans;
    for(int i=1;i<=M;i++){
        scanf("%s%d",op,&x);
        if(op[2]=='L') lct.access(x);
        else if(op[2]=='C') lct.mroot(root_now=x);
        else if(op[2]=='Q'){
            ans=0,len=0;
            if(l[x]<=l[root_now]&&r[root_now]<=r[x]){
                lct.splay(x);
                x=lct.nd[x].ch[0]?lct.find(lct.nd[x].ch[0],1):lct.nd[x].fa;
                if(l[x]>1) ans+=1.0*st.query(st.rt,1,N,1,l[x]-1),len+=l[x]-1;
                if(r[x]<N) ans+=1.0*st.query(st.rt,1,N,r[x]+1,N),len+=N-r[x];
                ans/=len;
            }
            else ans=1.0*st.query(st.rt,1,N,l[x],r[x])/(r[x]-l[x]+1);
            printf("%.10f\n",ans);
        }
    }
}
int main()
{
    data_in();
    work();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章