坠梦 | Falling into Dream
题目背景
神明愚弄凡间,所谓命运,不过是神明掷出的一颗骰子而已。
花朵等不到的蝴蝶,终究成了一分蹊跷的梦,一轮轮再次重启。
神明的提线木偶一次又一次的被扼住脖颈, 以爱的名义,消逝在时间的花海里。
无数的执念背后,都有一个被扭曲的“真理”。
你所承诺的没有出现,彻夜无眠,或许我只是自作主张的,替你爱了一次人间
“最虔诚者只祝祷,不虔诚者才有所求。”
没有过信仰,因为舍命救了一个人,有幸来到了天堂。
题目描述
给定一棵 n n n 个结点的无根树,每条边有非负整数边权。结点由 1 ∼ n 1 \sim n 1∼n 编号。
对于每一个点对 ( x , y ) (x, y) (x,y),定义 ( x , y ) (x, y) (x,y) 的距离 dis ( x , y ) \operatorname{dis}(x, y) dis(x,y) 为 x , y x,y x,y 两点之间唯一简单路径上边权的异或和。
给定两个结点 x , y x, y x,y,定义点 i i i 的价值 val x , y ( i ) \operatorname{val}_{x, y}(i) valx,y(i) 为 ( x , i ) (x, i) (x,i) 与 ( y , i ) (y, i) (y,i) 的距离的异或和,即
val x , y ( i ) = dis ( x , i ) ⊕ dis ( y , i ) 。 \operatorname{val}_{x, y}(i) = \operatorname{dis}(x, i) \oplus \operatorname{dis}(y, i) \textsf{。} valx,y(i)=dis(x,i)⊕dis(y,i)。
现在有 q q q 次询问,每次询问给出四个整数 x , y , l , r x, y, l, r x,y,l,r,求 ⨁ i = l r val x , y ( i ) \displaystyle \bigoplus_{i = l}^{r} \operatorname{val}_{x, y}(i) i=l⨁rvalx,y(i) 的值,即求
val x , y ( l ) ⊕ val x , y ( l + 1 ) ⊕ ⋯ ⊕ val x , y ( r − 1 ) ⊕ val x , y ( r ) 。 \operatorname{val}_{x, y}(l) \oplus \operatorname{val}_{x, y}(l + 1) \oplus \cdots \oplus \operatorname{val}_{x, y}(r - 1) \oplus \operatorname{val}_{x, y}(r) \textsf{。} valx,y(l)⊕valx,y(l+1)⊕⋯⊕valx,y(r−1)⊕valx,y(r)。
上述公式中, ⊕ \oplus ⊕ 表示二进制按位异或。
输入格式
第一行,两个整数 n , q n, q n,q。
接下来 n − 1 n - 1 n−1 行,每行三个整数 u , v , w u, v, w u,v,w,表示 u , v u, v u,v 之间有一条权值为 w w w 的边。
接下来 q q q 行,每行四个整数 x , y , l , r x,y,l,r x,y,l,r,表示一次询问。
输出格式
输出 q q q 行,每行一个整数,为每次询问的答案。
样例 #1
样例输入 #1
3 2
1 2 1
2 3 1
1 2 1 3
2 3 2 3
样例输出 #1
1
0
提示
【样例解释】
输入给出的树如上图所示。对于点对的距离,有
- dis ( 1 , 1 ) = dis ( 1 , 3 ) = dis ( 2 , 2 ) = dis ( 3 , 1 ) = dis ( 3 , 3 ) = 0 \operatorname{dis}(1, 1) = \operatorname{dis}(1, 3) = \operatorname{dis}(2, 2) = \operatorname{dis}(3, 1) = \operatorname{dis}(3, 3) = 0 dis(1,1)=dis(1,3)=dis(2,2)=dis(3,1)=dis(3,3)=0 以及
- dis ( 1 , 2 ) = dis ( 2 , 1 ) = dis ( 2 , 3 ) = dis ( 3 , 2 ) = 1 \operatorname{dis}(1, 2) = \operatorname{dis}(2, 1) = \operatorname{dis}(2, 3) = \operatorname{dis}(3, 2) = 1 dis(1,2)=dis(2,1)=dis(2,3)=dis(3,2)=1。
第 1 1 1 问: val 1 , 2 ( 1 ) ⊕ val 1 , 2 ( 2 ) ⊕ val 1 , 2 ( 3 ) = ( 0 ⊕ 1 ) ⊕ ( 1 ⊕ 0 ) ⊕ ( 0 ⊕ 1 ) = 1 ⊕ 1 ⊕ 1 = 1 \operatorname{val}_{1, 2}(1) \oplus \operatorname{val}_{1, 2}(2) \oplus \operatorname{val}_{1, 2}(3) = (0 \oplus 1) \oplus (1 \oplus 0) \oplus (0 \oplus 1) = 1 \oplus 1 \oplus 1 = 1 val1,2(1)⊕val1,2(2)⊕val1,2(3)=(0⊕1)⊕(1⊕0)⊕(0⊕1)=1⊕1⊕1=1。
第 2 2 2 问: val 2 , 3 ( 2 ) ⊕ val 2 , 3 ( 3 ) = ( 0 ⊕ 1 ) ⊕ ( 1 ⊕ 0 ) = 1 ⊕ 1 = 0 \operatorname{val}_{2, 3}(2) \oplus \operatorname{val}_{2, 3}(3) = (0 \oplus 1) \oplus (1 \oplus 0) = 1 \oplus 1 = 0 val2,3(2)⊕val2,3(3)=(0⊕1)⊕(1⊕0)=1⊕1=0。
【数据范围】
本题采用捆绑测试。
子任务编号 | n ≤ n \le n≤ | q ≤ q \le q≤ | 分值 |
---|---|---|---|
1 | 100 100 100 | 10 10 10 | 24 |
2 | 1 0 6 10^6 106 | 10 10 10 | 14 |
3 | 100 100 100 | 1 0 6 10^6 106 | 14 |
4 | 1 0 6 10^6 106 | 1 0 6 10^6 106 | 48 |
对于 100 % 100\% 100% 的数据,保证 1 ≤ n , q ≤ 10 6 1 \le n, q \le {10}^6 1≤n,q≤106, 1 ≤ u , v , x , y ≤ n 1 \le u, v, x, y \le n 1≤u,v,x,y≤n, 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1≤l≤r≤n, 0 ≤ w < 2 31 0 \le w < 2^{31} 0≤w<231。
【提示】
本题最大 I/O 量达到 60 MiB,请注意 I/O 效率。
思路
-
首先,这道题的公式有点多又很复杂,我们此时优化一下,也就是计算一下。
-
我们很容易发现: dis ( i , j ) = dis ( i , k ) ⊕ dis ( j , k ) ( i , j , k ∈ [ 1 , n ] ) \text{dis}(i,j)=\text{dis}(i,k)\oplus \text{dis}(j,k)( i,j,k\in[1,n]) dis(i,j)=dis(i,k)⊕dis(j,k)(i,j,k∈[1,n])。
-
这是为什么呢?
-
或者(含图片)
图片来源 -
细节1:本道题数据规模很大,我们最好用
scanf/printf
。 -
细节2:开 long long。
-
细节3:我们算
dist
是递归的时候算,而不是回溯的时候算,为什么呢?因为如果要回溯计算两个节点之间的距离,需要考虑到在一个树结构中,通常每个节点只有一条路径通向根节点,因此从自下而上的时候,无法在每一步确定唯一的路径。这样就会导致在回溯计算距离时出现多个可能的选择,从而无法保证得到正确的距离值,因此,我们在预处理阶段算树上两点距离都是用递归算。 -
细节4:因为是无根树,所以选哪个点当根 dfs 都可以。
-
细节5:因为是无根树树,所以只需要标记当前节点的父亲就可以,没必要再开一个
st
数组。 -
总结:对于公式很多的题,我们最好化简一下。
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int N = 1e6+10,M = 2*N;
int e[M],ne[M],w[M],h[N],idx;
bool st[N];
int n,q;
int dist[N];
int ans;
void add(int a,int b,int c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int fa){
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa)continue;
dist[j]=dist[u]^w[i];
dfs(j,u);
}
}
signed main(){
// cin>>n>>q;
scanf("%lld%lld",&n,&q);
memset(h,-1,sizeof h);
for(int i=1;i<n;i++){
int a,b,c;
// cin>>a>>b>>c;
// scanf("%lld%lld%lld",&a,&b,&c);
scanf(" %lld %lld %lld",&a,&b,&c);
// cout<<a<<" "<<b<<" "<<c<<endl;
add(a,b,c);
add(b,a,c);
}
dfs(1,-1);
while(q--){
int x,y,l,r;
// cin>>x>>y>>l>>r;
scanf("%lld%lld%lld%lld",&x,&y,&l,&r);
if((r-l+1)&1){
ans=dist[x]^dist[y];
}else{
ans=0;
}
// cout<<ans<<endl;
printf("%lld\n",ans);
}
return 0;
}