题意:
Dylans有一棵N个点的树。每个点有点权。树上节点标号为1∼N。
他得到了Q个询问,形式如下:
①0 x y:把第x个点的点权修改为y。
②1 x y:对于x∼y路径上的每一种点权,是否都出现偶数次?
保证每次询问的路径上最多只有一种点权的出现次数是奇数次。
1≤N,Q≤100000, 点权A[i]∈N,且都 ≤100000
解析:
这题比较难啊,参考了一下网络上面的题解。
如果一个数组, 只有一个数字出现奇数次, 有什么比较好的方法 快速找出这个数字?
答:因为两个相同的数异或和等于0,所以把所有的数字异或在一起,异或和就是,出现的奇数数字。
那么能想到用线段树来维护异或和的方法,来解决这一题。有一种经典的将树上的点转化成序列的方法,我们用dfs遍历这棵树,那么对于一个节点,他一点比他的子树即子节点先访问到,且当他的最后一个子节点的所有子树也都访问完时,这中间访问的节点一定都是他的子树。那么我们可以在访问时做一下记录,每个点首先被访问的index和结束时的index,那么这中间的便是他的子树。
转换成序列之后就能利用线段树进行维护了。
具体做法就是:
先用dfs一遍求出,到每个点的dfs序,记录下该dfs序,以便之后用dfs维护。
然后构建一个线段树,来表示每个区间的异或和。
然后修改 (u,x) 的话,就直接利用线段树进行区间更新,注意修改的值为 val[u]⊕x ,因为 val[u] 和子树异或过了,如果想要修改u并更新子树,就必须先消去 val[u] ,然后再异或上x。
如果查询 (u,v) 的话,就 ans=query(st[u],ed[u])⊕query(st[v],ed[v])⊕val[LCA(u,v)]
注意
这题有坑的地方就是点权可能为0,处理方法是先把所以的点权都加上1,最后再减1。
my code
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#define ls o<<1
#define rs o<<1|1
#define lson o<<1,L,M
#define rson o<<1|1,M+1,R
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int n,q;
int val[N];
vector<int> G[N];
int st[N], ed[N];
namespace LCA {
int first[N], node[N*2], deep[N*2];
struct ST {
int dp[N*2][20];
void init(int n) {
for(int i = 1; i <= n; i++) dp[i][0] = i;
for(int k = 1; (1 << k) <= n; k++) {
for(int i = 1; i <= n-(1<<k)+1; i++) {
int a = dp[i][k-1];
int b = dp[i+(1<<(k-1))][k-1];
dp[i][k] = (deep[a] < deep[b]) ? a : b;
}
}
}
int query(int L, int R) {
int len = (R-L+1), k = 0;
while((1<<(k+1)) <= len) k++;
int a = dp[L][k], b = dp[R-(1<<k)+1][k];
return (deep[a] < deep[b]) ? a : b;
}
} table;
int clock, index;
void dfs(int u, int pre, int de) {
st[u] = ++index;
node[++clock] = u;
deep[clock] = de;
first[u] = clock;
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if(v == pre) continue;
dfs(v, u, de+1);
node[++clock] = u;
deep[clock] = de;
}
ed[u] = index;
}
void init(int n) {
clock = 0;
index = 0;
dfs(1, -1, 0);
table.init(2*n-1);
}
int lca(int u, int v) {
int L = first[u], R = first[v];
if(L > R) swap(L, R);
return node[table.query(L, R)];
}
}
struct Node{
int val, set;
} node[N<<2];
inline void pushUp(Node& fa, Node& Ls, Node& Rs){
fa.val = (Ls.val ^ Rs.val);
}
inline void pushDown(Node& fa, Node& Ls, Node& Rs){
if (fa.set) {
Ls.val ^= fa.set; Ls.set ^= fa.set;
Rs.val ^= fa.set; Rs.set ^= fa.set;
fa.set = 0;
}
}
void modify(int o, int L, int R, int ql, int qr, int delta){
if (ql <= L && R <= qr) {
node[o].val ^= delta;
node[o].set ^= delta;
return;
}
int M = (L+R)/2;
pushDown(node[o], node[ls], node[rs]);
if (ql <= M) modify(lson, ql, qr, delta);
if (qr > M) modify(rson, ql, qr, delta);
pushUp(node[o], node[ls], node[rs]);
}
int query(int o, int L, int R,int ql, int qr){
if(ql<=L && R<=qr){
return node[o].val;
}
int M = (L+R)/2;
pushDown(node[o], node[ls], node[rs]);
int ret = 0;
if(qr <= M) ret ^= query(lson,ql,qr);
if(ql > M) ret ^= query(rson,ql,qr);
return ret;
}
void addEdge(int u, int v) {
G[u].push_back(v);
}
int MAIN() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) G[i].clear();
int u, v;
for(int i=1;i<=n-1;i++){
scanf("%d%d",&u,&v);
addEdge(u, v);
addEdge(v, u);
}
for(int i=1;i<=n;i++){
scanf("%d",&val[i]);
val[i]++;
}
LCA::init(n);
memset(node,0,sizeof(node));
for(int i=1;i<=n;i++)
modify(1,1,n,st[i],ed[i],val[i]);
while(q--) {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if(a == 0) {
c++;
modify(1,1,n,st[b],ed[b],val[b]^c);
val[b] = c;
}else {
int wt1 = query(1,1,n,st[b],st[b]);
int wt2 = query(1,1,n,st[c],st[c]);
int wt3 = val[LCA::lca(b,c)];
int ans = wt1^wt2^wt3;
printf("%d\n",ans-1);
}
}
}
return 0;
}
const int main_stack = 16;
char my_stack[128<<20];
int main() {
__asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");
__asm__("movl %%eax, %%esp;\n"::"a"(my_stack + sizeof(my_stack) - main_stack):"%esp");
MAIN();
__asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");
return 0;
}