由于只有一种点权是奇数次,故可以用异或做树上差分
现在来考虑怎样维护操作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的写的位置