Problem( Link-cut tree )

有一棵n個節點的樹,每個節點有一個顏色,初始每個節點顏色不相同,且以節點1爲根。定義每個點的權值爲這個點到根的路徑上不同顏色的個數。現在進行m次操作,每次操作爲下列三種之一:
1、將x到當前根路徑上的所有點染成一種新的顏色;
2、將x到當前根路徑上的所有點染成一種新的顏色,並且把這個點設爲新的根;
3、查詢以x爲根的子樹中所有點權值的平均值。

Input

輸入一共n+m行。
第一行兩個數n,m。
接下來n-1行每行兩個數a,b,表示點a和點b之間有一條邊。
接下來m行每行兩個數t,x,t表示操作類型。

Output

對於每個操作3,輸出對應的答案,輸出與答案的絕對誤差不超過10^-6即爲正確。

Sample Input

8 6
1 2
1 3
2 8
3 4
3 5
3 6
4 7
3 7
1 3
3 3
2 5
1 2
3 1

Sample Output

4.0000000000
2.0000000000
1.3333333333

Data Constraint

對於20%的數據:n <= 100,m <= 100
對於另30%的數據:n <= 50000,m <= 50000,且不存在操作2,其中有15%的數據每次僅會詢問一個節點
對於另30%的數據:整棵樹爲一條鏈,其中有15%的數據不存在操作2
對於100%的數據:n <= 100000,m <= 100000

做法:
它的染色手段與動態樹的access操作明顯等價。重鏈連的點同色。
先預處理出dfs序,以dfs序爲基礎建線段樹,在access的同時維護每個點的權值。
至於換根操作,在LCT裏可以直接Evert( ) ; 對於查詢區間的變化,分類討論:
<1>Now_Root == x :: ask the whole interval
<2>Now_Root is in the subtree of x in origin graph :: we can figure out the son of x , named u , which leads us to Now_Root by redoubling algorithm . ask the complementary of u ‘s interval
<3>else : ask the interval of x

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std ;

#define N 100010

typedef long long ll ;

int n , m , i , j , k , g[N] , T ;

struct edge {
    int y , l ;
}f[N*2];

int root = 1 ;

void ins( int x , int y ) {
    f[++T].y = y , f[T].l = g[x] , g[x] = T ; 
}

int fa[N] , de[N] , seq[2*N] , lower[N] , upper[N] , dfn , l[32][N] , inx[32] ;

void dfs( int po ) {
    int k ; 
    seq[ ++ dfn ] = po ;
    lower[po] = dfn ;
    l[0][po] = fa[po] ;
    for( k=1 ; inx[k]<=de[po] ; k++ ) l[k][po] = l[k-1][ l[k-1][po] ] ;
    for( k=g[po] ; k ; k=f[k].l ) if( f[k].y!=fa[po] ) {
        fa[ f[k].y ] = po , de[ f[k].y ] = de[po] + 1 ;
        dfs( f[k].y ) ;
    }
    seq[ ++ dfn ] = po ;
    upper[po] = dfn ;
}

struct LCT {
    int l , r , fa ; 
    bool rev ;
}tr[N] ;

bool ROOT( int po ) {
    int f = tr[po].fa ;
    if( tr[f].l!=po && tr[f].r!=po ) return 1 ;
    return 0 ;
}

void Change( int po ) {
    swap( tr[po].l , tr[po].r ) ;
    tr[po].rev ^= 1 ;
}

void SendDown( int po ) {
    if( tr[po].rev ) {
        tr[po].rev = 0 ;
        if( tr[po].l ) Change( tr[po].l ) ;
        if( tr[po].r ) Change( tr[po].r ) ;
    }
}

void Rotate( int po ) {
    int ps ;
    ps = tr[po].fa ;
    SendDown( ps ) , SendDown( po ) ;   
    if( tr[ps].r==po ) {
        int l = tr[po].l ;
        tr[ps].r = l ;
        tr[l].fa = ps ;
        tr[po].fa = tr[ps].fa ;
        if( ROOT( ps ) == 0 ) {
            LCT *f = &tr[ tr[po].fa ] ;
            if( f->r==ps ) f->r = po ; else f->l = po ;
        }
        tr[ps].fa = po ;
        tr[po].l = ps ;
    } else {
        int r = tr[po].r ;
        tr[ps].l = r ;
        tr[r].fa = ps ;
        tr[po].fa = tr[ps].fa ;
        if( ROOT( ps ) == 0 ) {
            LCT *f = &tr[ tr[po].fa ] ;
            if( f->r==ps ) f->r = po ; else f->l = po ;
        }
        tr[ps].fa = po ;
        tr[po].r = ps ;
    }
}

void splay( int x ) {
    SendDown( x ) ;
    for( int i = x ; ROOT( i ) == 0 ;  ) {
        int j = tr[i].fa , k = tr[j].fa ;
        SendDown( i ) ;
        if( ROOT( j ) ) {
            SendDown( j ) ;
            Rotate( i ) ;
            continue ;
        }
        SendDown( k ) , SendDown( j ) ;
        if( tr[j].l==i && tr[k].l==j || tr[j].r==i && tr[k].r==j ) {
            Rotate( j ) , Rotate( i ) ; 
        } else {
            Rotate( i ) , Rotate( i ) ;
        }

    }
}

ll sum[ 8 * N ] , mark[ 8*N ] ;

void Effect( int po , int l , int r ) {
    sum[po] += mark[po] * ( r-l+1 ) ; 
    if( l!=r ) mark[ po * 2 + 1 ] += mark[po] , mark[ po * 2 ] += mark[po] ;
    mark[po] = 0 ;
}

void Maintain( int po ) {
    sum[po] = sum[po*2+1] + sum[po*2] ;
} 


void Modify( int _l , int _r , int l , int r , int delta , int po ) {
    if( _l==0 ) return ;
    if( _l>_r ) return ; 
    Effect( po , l , r ) ;
    if( _l==l && _r==r ) {
        mark[po] += delta ;
        Effect( po , l , r ) ;
        return ;
    }
    int m = ( l+r ) / 2 ;
    if( _r<=m ) Modify( _l,_r,l,m,delta,po*2 ) ;
        else if( _l>m ) Modify( _l,_r,m+1,r,delta,po*2+1 ) ;
            else Modify( _l,m,l,m,delta,po*2 ) , Modify( m+1,_r,m+1,r,delta,po*2+1 ) ;
    Effect( po * 2 , l , m ) , Effect( po*2+1 , m+1 , r ) ;
    Maintain( po ) ;
}

void Procedure( int z , int va ) {
    if( upper[z] > upper[root] && lower[z] < lower[root] ) {
        int dire = root ;
        for( int sub = de[root]-de[z]-1 , j = 0 ; sub ; sub/=2 , j++ ) if( sub % 2 ) {
            dire = l[j][dire] ;
        }
        Modify( 1 , lower[dire]-1 , 1 , 2*n , va , 1 ) ;
        Modify( upper[dire]+1 , 2*n , 1 , 2*n , va , 1 ) ;
    } else {
        Modify( lower[z] , upper[z] , 1 , 2*n , va , 1 ) ;
    }
}

void access( int x ) {
    for( int u = x , v = 0  ; u  ; v = u , u = tr[u].fa ) {
        splay( u ) ;
        int i ; 
        SendDown( u ) ;
        for( i=tr[u].r ; tr[i].l ; i=tr[i].l ) SendDown( i ) ;
        Procedure( i , 1 ) ;
        for( i=v ; tr[i].l ; i=tr[i].l ) SendDown( i ) ;
        Procedure( i , -1 ) ;
        tr[u].r = v ;
    }

}


void Evert( int x ) {
    access( x ) ;
    splay( x ) ;
    Change( x ) ;
}

ll Quest( int _l , int _r , int l , int r , int po ) {
    Effect( po , l , r ) ;
    if( _l==l && _r==r ) return sum[po] ;
    int m = ( l+r ) / 2 ;
    if( _r<=m ) return Quest( _l,_r,l,m,po*2 ) ;
        else if( _l>m ) return Quest( _l,_r,m+1,r,po*2+1 ) ;
            else return Quest( _l,m,l,m,po*2 ) + Quest( m+1,_r,m+1,r,po*2+1 ) ;
} 

int main() {freopen("k.in","r",stdin ) ;
    for( i=1 , inx[0]=1 ; i<31 ; i++ ) inx[i] = inx[i-1] * 2 ;
    scanf("%d%d",&n,&m ) ;
    for( i=1 ; i<n ; i++ ) {
        int x , y ;
        scanf("%d%d",&x,&y ) ;
        ins( x , y ) , ins( y , x ) ;
    }
    dfs( 1 ) ; 
    for( i=1 ; i<=n ; i++ ) tr[i].fa = fa[i] , Modify( lower[i] , lower[i] , 1 , 2*n , de[i]+1 , 1 ) ,
                           Modify( upper[i] , upper[i] , 1 , 2*n , de[i]+1 , 1 ) ;

    while( m-- ) {
        int x , y ;
        scanf("%d%d",&x , &y ) ;
        if( x==1 ) access( y ) ;
        if( x==2 ) Evert( y ) , root = y ;
        if( x==3 ) {
            double ans ;
            if( y==root ) {
                ans = Quest( 1 , 2*n , 1 , 2*n , 1 ) / double( 2 * n ) ;
                printf("%.8lf\n",ans ) ;
                continue ;
            }
            if( upper[y] > upper[root] && lower[y] < lower[root] ) {
                int dire = root ;
                for( int sub = de[root]-de[y]-1 , j = 0 ; sub ; sub/=2 , j++ ) if( sub % 2 ) {
                    dire = l[j][dire] ;
                }
                ans =  ( Quest( 1 , 2*n , 1 , 2*n , 1 ) - Quest( lower[dire] , upper[dire] , 1 , 2*n , 1 ) ) / double( 2 * n - ( upper[dire] - lower[dire] + 1 ) ) ; 
            } else {
                ans = Quest( lower[y] , upper[y] , 1 , 2*n , 1 ) / double( upper[y] - lower[y] + 1 ) ;

            }
            printf("%.8lf\n",ans ) ;
        }
    }
}

Debug log ::

<1>Rotate前忘了SendDown( ps ),SendDown( po )
<2>Splay 時
if( ROOT( j ) ) {
Rotate( i )
continue
}
將習慣性判斷寫成if( k ) {
這個細節很容易犯,還有rotate更新祖父兒子節點的值是也要用ROOT判根
<3>access時一路往左兒子方向走時沒有SENDDOWN
要記住在動態樹上走時,只要是在那樹上走,時時刻刻要記得傳標記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章