坠梦 | Falling into Dream(思维题+数学): 许多 细节

108 篇文章 0 订阅

坠梦 | Falling into Dream

题目背景

神明愚弄凡间,所谓命运,不过是神明掷出的一颗骰子而已。

花朵等不到的蝴蝶,终究成了一分蹊跷的梦,一轮轮再次重启。

神明的提线木偶一次又一次的被扼住脖颈, 以爱的名义,消逝在时间的花海里。

无数的执念背后,都有一个被扭曲的“真理”。

你所承诺的没有出现,彻夜无眠,或许我只是自作主张的,替你爱了一次人间

“最虔诚者只祝祷,不虔诚者才有所求。”

没有过信仰,因为舍命救了一个人,有幸来到了天堂。

题目描述

给定一棵 n n n 个结点的无根树,每条边有非负整数边权。结点由 1 ∼ n 1 \sim n 1n 编号。

对于每一个点对 ( 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=lrvalx,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(r1)valx,y(r)

上述公式中, ⊕ \oplus 表示二进制按位异或。

输入格式

第一行,两个整数 n , q n, q n,q

接下来 n − 1 n - 1 n1 行,每行三个整数 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)=(01)(10)(01)=111=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)=(01)(10)=11=0


【数据范围】

本题采用捆绑测试。

子任务编号 n ≤ n \le n q ≤ q \le q分值
1 100 100 100 10 10 1024
2 1 0 6 10^6 106 10 10 1014
3 100 100 100 1 0 6 10^6 10614
4 1 0 6 10^6 106 1 0 6 10^6 10648

对于 100 % 100\% 100% 的数据,保证 1 ≤ n , q ≤ 10 6 1 \le n, q \le {10}^6 1n,q106 1 ≤ u , v , x , y ≤ n 1 \le u, v, x, y \le n 1u,v,x,yn 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1lrn 0 ≤ w < 2 31 0 \le w < 2^{31} 0w<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;
}
  • 29
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值