文章标题

本文介绍了一种使用链剖解决特定树形结构问题的方法,通过维护节点权值的变化及路径查询来实现高效处理。针对含有大量节点和复杂操作的场景,提出了一种新颖的数据处理策略,即将不同权值的操作独立处理,并利用线段树进行优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给定一棵N 个节点的树,标号从1~N。每个点有一个权值。要求维护两种操作:

  1. C i x(0<=x<2^31) 表示将i 点权值变为x

  2. Q i j x(0<=x<2^31) 表示询问i 到j 的路径上有多少个值为x 的节点

N <= 100000

显然要用链剖, 然而直接做貌似不行,由于询问的权值只涉及到与那个权值相关的点与操作,考虑每个权值独立做,对于操作1,可以看做是在原权值上删点,新权值加点。
一个权值做完做下一个权值时,线段树可以打个clear标记表示清0 。
独立做这个想法可真是棒啊。

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

using namespace std ;

#define N 200010
#define psb( x )  push_back( x ) 
int i , j , k , Q , n , g[N] , T ; 

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

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

int w[N] ;

struct stream {
    int x , y , va ;
    int id ;
}z[ N * 3 ] ;


char s[2] ;

bool cmp( stream a , stream b ) {
    return  a.va < b.va ;
}

bool _cmp( stream a , stream b ) {
    return  a.id < b.id ;
}

int tmp[N*3] ;

struct Operation {
    int typ , x , y , ori ;
    // typ : 1 delete 
    //   2 Insert 
    //   3 Query
};

vector< Operation > seq[ N * 3 ] ;

int l[32][N] , de[N] , fa[N] , tid[N] , top[N] , siz[N] , son[N] , dfn , inx[32] ;

void Find_Edge( int po ) {
    int ax = 0  ;  
    siz[po] = 1 ;
    l[0][po] = fa[po] ;
    for( i=1 ; i<=30 && de[po] >= inx[i] ; i++ ) l[i][po] = l[i-1][ l[i-1][po] ] ;
    for( int k = g[po] ; k ; k = f[k].l ) if( fa[po]!=f[k].y ) {
        de[ f[k].y ] = de[po] + 1 ;
        fa[ f[k].y ] = po ;
        Find_Edge( f[k].y ) ;
        if( siz[ f[k].y ] > ax ) {
            ax = siz[ f[k].y ] ;
            son[po] = f[k].y ;
        }
        siz[po] += siz[ f[k].y ] ;
    }
}

void Mark_Edge( int po , int anc ) {
    tid[po] = ++ dfn ;
    top[po] = anc ;
    if( son[po] != 0 ) Mark_Edge( son[po] , anc ) ;
    for( int k = g[po] ; k ; k=f[k].l ) if( f[k].y != fa[po] && f[k].y != son[po] ) {
        Mark_Edge( f[k].y , f[k].y ) ;
    }
}

int LCA( int x , int y ) {
    if( de[x] > de[y] ) swap( x , y ) ;
    for( int sub = de[y] - de[x] , j = 0 ; sub ; sub /= 2 , j++ ) if( sub % 2 ) 
        y = l[j][y] ;
    if( y==x ) return x ;
    for( int i = 1 ; i>=0 ; ) {
        for( i=30 ; i>=0 ; i-- ) if( l[i][x]!=l[i][y] ) {
            x = l[i][x] , y = l[i][y] ;
            break ;
        }
    }
    return fa[x] ;
}

int sum[N*4] ;

int ans[N] ;

bool clr[N*4] ;

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

void Clear( int po ) {
    clr[po] = 1 ;
    Effect( 1 , n , po ) ;
}

void Insert( int l , int r , int tar , int delta , int p ) {
    int m = ( l + r ) / 2 ;
    Effect( l , r , p ) ;
    if( l==r ) {
        sum[p] += delta ;
        return ;
    }
    if( tar <= m )  Insert( l , m , tar , delta , p*2 ) ;
        else Insert( m+1 , r , tar , delta , p*2+1 )  ;
    Effect( l , m , p * 2 ) , Effect( m+1 , r , p * 2 + 1 ) ;
    sum[p] = sum[p*2] + sum[p*2+1] ;
}

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

int Chain_Process( int u , int v ) {
    int lca = LCA( u , v ) ;
    int k = 2 , ret = 0 ;
    while( k-- ) {
        while( top[u]!=top[lca] ) {
            ret += Ask( 1 , n , tid[ top[u] ] , tid[u] , 1 ) ;
            u = fa[ top[u] ] ;
        }
        ret += Ask( 1 , n , tid[ lca ] , tid[u] , 1 ) ;
        swap( u , v ) ;
    }
    ret -= Ask( 1 , n , tid[ lca ] , tid[ lca ] , 1 ) ;
    return ret ;
}

int main() {freopen("k.in","r",stdin ) ;freopen( "k.out","w",stdout ) ;
    for( i=1,inx[0] = 1  ; i<=31 ; i++ ) inx[i] = inx[i-1] *  2 ;
    scanf("%d%d",&n,&Q ) ;
    for( i=1 ; i<=n ; i++ ) scanf("%d",&w[i] ) ;
    for( i=2 ; i<=n ; i++ ) {
        int x , y ;
        scanf("%d%d" , &x,&y ) ;
        Ins( x ,y ) ;
    }
    for( i=1 ; i<=Q ; i++ ) {
        scanf("%s",s ) ;
        z[i].id = i ;
        if( s[0] == 'Q' ) {
            scanf("%d%d%d",&z[i].x , &z[i].y , &z[i].va ) ;
        } else {
            scanf("%d%d",&z[i].x,&z[i].va ) ;
        }
    }
    for( i=1 ; i<=n ; i++ ) {
        z[++Q].x = i , z[Q].va = w[i] ;
        z[Q].id = -1 ;
        z[Q].y = i ;
    }
    sort( z+1 , z+1+Q , cmp ) ;
    int disva = 0 ;
    for( i=1 ; i<=Q ; i++ ) {
        if( z[i].va != z[i-1].va ) ++disva ;
        tmp[i] = disva ;
    }
    for( i=1 ; i<=Q ; i++ ) z[i].va = tmp[i] ;
    sort( z+1 , z+1+Q , _cmp ) ;
    i = 1 ; 
    while( z[i].id == -1 ) {
        Operation P ;
        P.typ = 2 ;
        P.x = z[i].x ;
        seq[ z[i].va ].psb( P ) ;
        w[ z[i].y ] = z[i].va ;
        i++ ;
    }
    for( ; i<=Q ; i++ ) {
        Operation P ;
        if( z[i].y ) {
            P.x = z[i].x , P.y = z[i].y , P.typ = 3 , P.ori = z[i].id ;
            seq[ z[i].va ].psb( P ) ;
        } else {
            P.x = z[i].x , P.typ = 1 ;
            seq[ w[ z[i].x ] ].psb( P ) ;
            P.typ = 2 ;
            w[ z[i].x ] = z[i].va ;
            seq[ z[i].va ].psb( P ) ;
        }
    }
    Find_Edge( 1 ) ;
    Mark_Edge( 1,1 ) ;
    memset( ans , 255 , sizeof ans ) ;
    int cnt = 0 ;
    for( i=1 ; i<=disva ; i++ ) {
        Clear( 1 ) ;
        for( vector< Operation >::iterator it = seq[i].begin() ; it != seq[i].end() ; it++ ) {
            if( it->typ== 1 ) {
                Insert( 1 , n , tid[ it->x ] , -1 , 1 ) ;
            } else if( it->typ==2 ) {
                Insert( 1 , n , tid[ it->x ] , 1 , 1 ) ;
                cnt ++ ;
            } else {
                ans[ it->ori ] = Chain_Process( it->x , it->y ) ;

            }
        }
    }
    for( i=1 ; i<=Q ; i++ ) if( ans[i] != -1  ) printf("%d\n",ans[i] ) ;    
}

    // typ : 1 delete 
    //   2 Insert 
    //   3 Query
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值