TREE(dfs序+树上差分)

题目

一棵 N 个节点的树,每个节点有整数值的点权。树上节点标号为 1 N
Q 个询问,形式如下:
(1) 0 x y :把编号 x 的节点的点权修改为 y
(2) 1 x y :对于编号 x y 路径上的每一种点权,是否都出现偶数次?
数据保证每次询问的路径上最多只有 一种 点权的出现次数是奇数次。
输入格式:
第一行两个数 N Q 表示树的节点数和询问个数。 (5<=N,Q<=100000)
接下来 N-1 行,每行一对数 (x,y) 表示树上的一条边。
接下来一行 N 个整数表示每个点的点权。 (0<= 点权 <=100000)
接下来 Q , 每行三个数 (opt,x,y) 表示一个询问。
输出格式:
对于每个 (2) 询问,如果全是偶数输出“ -1" ,否则输出出现奇数次的 权值
 

题解

由于只有一种点权是奇数次,故可以用异或做树上差分

现在来考虑怎样维护操作1

这里有一个之前不知道的用法:

dfs序可以对每一个点维护时间戳(也就是访问到这个点时的时间的in以及访问完这个点子树内所有点后的时间的out,这里给定一个时间单位只能访问一个点),那么时间戳in——out中间的点都是在这个点的子树内

而对于修改一个点i的权值,只有在i的子树内的前缀和数组才会改变,且改变的值是一样的,故可以用线段树或树状数组维护区间修改

代码

//树上差分首先要维护前缀和数组以及LCA
//还要注意一个点,也就是权值可以为0,那这样的话可以把所有的权值都加1,然后输出的时候减1,就不影响异或了
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 100003;
int fa[MAXN][23] , high[MAXN];
vectorG[MAXN];
int n , Q;
int in_[MAXN] , out_[MAXN] , ncnt;//时间戳
long long sum[MAXN];
int V[MAXN];
struct node{
    int l , r , lazy , val;
}tre[MAXN*4];
void read( int &x ){
    x = 0;char s = getchar();
    while( s < '0' || s > '9' ) s = getchar();
    while( s >= '0' && s <= '9' ){
        x = x * 10 + s -'0';
        s = getchar();
    }
}
void dfs( int x , int f ){
    in_[x] = ++ncnt;//进入这个子树的时间
    bool flag = 0;
    for( int i = 0 ;  i< G[x].size() ; i ++ ){
        int v = G[x][i];
        if( v == f ) continue;
        flag = 1;
        fa[v][0] = x;
        sum[v] = sum[x] ^ V[v];
        high[v] = high[x] + 1;
        dfs( v , x );
        out_[x] = out_[v];
    }
    if(!flag )
        out_[x] = in_[x];
    //out_[x] = ncnt 即可,不用flag太麻烦,也就是访问完这颗子树出来时的时间
}
//LCA模板
void jump( int &x , int mu ){
    for( int i = 20; i >= 0 ; i -- ){
        if( high[fa[x][i]] >= mu )
            x = fa[x][i];
    }
}
int LCA( int x , int y ){
    if( high[x] > high[y] ) jump( x , high[y] );
    else if( high[x] < high[y] ) jump( y , high[x] );
    if( x == y ) return x;
    for( int i = 20 ; i >= 0 ; i -- ){
        if( fa[x][i] != fa[y][i] )
            x = fa[x][i] , y = fa[y][i];
    }
    return fa[x][0];
}
void pushdown( int i ){
    if( tre[i].lazy ){
        tre[i*2].val ^= tre[i].lazy;
        tre[i*2+1].val ^= tre[i].lazy;
        tre[i*2].lazy ^= tre[i].lazy;
        tre[i*2+1].lazy ^= tre[i].lazy;
        tre[i].lazy = 0 ;
    }
}
//线段树模板,只是这里平时用的是加,变成了异或而已
void build( int i , int l , int r ){
    tre[i].l = l , tre[i].r = r;
    tre[i].lazy = 0;
    if( l == r ){
        tre[i].val =0 ;//注意修改是在原基础上修改,所以初值赋0
        return ;
    }
    int mid = ( l + r ) / 2;
    build( i * 2 , l , mid );
    build( i * 2 + 1 , mid + 1 , r );
    tre[i].val = tre[i*2].val ^ tre[i*2+1].val;
}
void change( int i , int l , int r , int x ){
    if( tre[i].r < l || tre[i].l > r ) return ;
    if( tre[i].l >= l && tre[i].r <= r ){
        tre[i].val ^= x;
        tre[i].lazy ^= x;
        return ;
    }
    pushdown( i );
    change( i * 2 , l , r , x );
    change( i * 2 + 1 , l , r , x );
    tre[i].val = tre[i*2].val ^ tre[i*2+1].val;
}
int query( int i , int l , int r ){//这里用单点修改也可以
    if( tre[i].r < l || tre[i].l > r ) return -1;
    if( tre[i].l >= l && tre[i].r <= r ){
        return tre[i].val;
    }
    pushdown( i );
    int ans = -1;
    ans = query( i * 2 , l , r );
    if( ans != -1 ) return ans;
    ans = query( i * 2 + 1 , l , r );
    return ans;
}
int main()
{
    freopen( "tree.in" , "r" , stdin );
    freopen( "tree.out" , "w" , stdout );
    scanf( "%d%d" , &n , &Q );
    for( int i = 1 ; i < n ; i ++ ){
        int x , y;
        scanf( "%d%d" , &x , &y );
        G[x].push_back( y );
        G[y].push_back( x );
    }
    for( int i = 1 ; i <= n  ; i ++ ){
        scanf( "%d" , &V[i] );
        V[i] ++;
    }
    high[1] = 1;
    sum[1] = V[1];
    dfs( 1 , 0 );
    for( int j = 1; j <= 20 ; j ++ )
        for( int i = 1 ; i <= n ; i ++ )
            fa[i][j] = fa[fa[i][j-1]][j-1];
    build( 1 , 1 , n );
    while( Q -- ){
        int opt , x , y;
        read( opt ); read( x );read( y );
        if( !opt ){
            change( 1 , in_[x] , out_[x] , V[x] ^ ( y + 1 ) );//要先删去原来的V【x】把它变为(y+1)
            V[x] = y + 1;
        }
        else{
            int lca = LCA( x , y );
            int xsum = sum[x] ^ query( 1 , in_[x] , in_[x] ) , ysum = sum[y] ^ query( 1 , in_[y] , in_[y] );
            int ans = xsum ^ ysum ^ V[lca];//画图理解即可
            if( ans > 0 ){
                printf( "%d\n" , ans - 1 );
            }
            else
                printf( "-1\n" );
        }
    }
    return 0;
}

注意pushdown的写的位置 

 

你可能感兴趣的:(dfs序,线段树,总结)