题目来源
Description
- 给定一棵 n n n 个节点的无根树,边权为非负整数,节点编号 [ 1 , n ] [1,n] [1,n]。
- 对于给定的 q q q 组询问 ( x , y , l , r ) (x,y,l,r) (x,y,l,r),求 ⨁ i = l r ( d i s ( x , i ) ⊕ d i s ( y , i ) ) \bigoplus_{i=l}^r\Big(dis(x,i)\oplus dis(y,i)\Big) ⨁i=lr(dis(x,i)⊕dis(y,i)),其中 d i s ( x , y ) dis(x,y) dis(x,y) 表示 x x x 到 y y y 的唯一简单路径上边的边权异或和。
- 1 ≤ n , q ≤ 1 0 6 1\le n,q\le10^6 1≤n,q≤106。
Solution
首先考虑如何用 O ( 1 ) O(1) O(1) 的效率求出 d i s ( x , y ) dis(x,y) dis(x,y),于是我们可以很容易地想到设 f i f_i fi 表示节点 i i i 到根节点的路径上边的边权异或和,则显然 d i s ( x , y ) = f x ⊕ f y dis(x,y)=f_x\oplus f_y dis(x,y)=fx⊕fy。
简单证明一下其正确性:若设 l c a lca lca 表示 x x x 与 y y y 的最近公共祖先,则显然 d i s ( x , y ) = d i s ( x , l c a ) ⊕ d i s ( y , l c a ) dis(x,y)=dis(x,lca)\oplus dis(y,lca) dis(x,y)=dis(x,lca)⊕dis(y,lca),那么由于 a ⊕ a = 0 , a ⊕ 0 = a a\oplus a=0,a\oplus0=a a⊕a=0,a⊕0=a,且异或运算满足结合律,则 d i s ( x , y ) dis(x,y) dis(x,y) 就可以转化为 f x ⊕ f l c a ⊕ f y ⊕ f l c a f_x\oplus f_{lca}\oplus f_y\oplus f_{lca} fx⊕flca⊕fy⊕flca 即 d i s ( x , y ) = f x ⊕ f y dis(x,y)=f_x\oplus f_y dis(x,y)=fx⊕fy。
则我们只需 O ( n ) O(n) O(n) 预处理出 { f n } \{f_n\} {fn} 即可。
再考虑如何求出答案。
⨁
i
=
l
r
(
d
i
s
(
x
,
i
)
⊕
d
i
s
(
y
,
i
)
)
=
⨁
i
=
l
r
(
f
x
⊕
f
i
⊕
f
y
⊕
f
i
)
=
⨁
i
=
l
r
(
f
x
⊕
f
y
)
\begin{aligned} \bigoplus_{i=l}^r\Big(dis(x,i)\oplus dis(y,i)\Big) &=\bigoplus_{i=l}^r\Big(f_x\oplus f_i\oplus f_y\oplus f_i\Big)\\ &=\bigoplus_{i=l}^r\Big(f_x\oplus f_y\Big) \end{aligned}
i=l⨁r(dis(x,i)⊕dis(y,i))=i=l⨁r(fx⊕fi⊕fy⊕fi)=i=l⨁r(fx⊕fy)
根据异或的性质,若
r
−
l
+
1
r-l+1
r−l+1 为偶数,显然答案即为
0
0
0;否则答案就为
f
x
⊕
f
y
f_x\oplus f_y
fx⊕fy,可以
O
(
1
)
O(1)
O(1) 处理每一次询问。
则总效率为 O ( n + q ) O(n+q) O(n+q)。
Code
#include <bits/stdc++.h>
using namespace std;
int n,q,head[1000005],ver[2000005],edge[2000005],nxt[2000005],tot,f[1000005];
void add(int u,int v,int w){
ver[++tot]=v,edge[tot]=w,nxt[tot]=head[u],head[u]=tot;
}
void work(int u,int fa){
for (int i=head[u];i;i=nxt[i]){
int v=ver[i],w=edge[i];
if (v!=fa) f[v]=f[u]^w,work(v,u);
}
}
int main(){
scanf("%d%d",&n,&q);
for (int i=1,u,v,w;i<n;i++) scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w);
work(1,0);
while (q--){
int x,y,l,r;
scanf("%d%d%d%d",&x,&y,&l,&r);
printf("%d\n",(r-l+1)%2==0?0:f[x]^f[y]);
}
return 0;
}