Dijkstra(堆优化)或DFS解决洛谷——P8965 坠梦 | Falling into Dream

蒟蒻来讲题,还望大家喜。若哪有问题,大家尽可提!

Hello, 大家好哇!本初中生蒟蒻用Dijkstra的堆优化版来解决洛谷P8965 坠梦 | Falling into Dream这一道题!

===========================================================================================

坠梦 | 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 效率。


思路

我们要知道异或的性质:
a ⊕ a = 0 a\oplus a = 0 aa=0
那么, a ⊕ b ⊕ a = b a\oplus b \oplus a = b aba=b
这一条性质很重要!
因为,本题是一棵无根树,所以,对于树上的任意点x到任一点y之间最多只有一条路径,假设为 x − > i − > y x->i->y x>i>y(如图):
在这里插入图片描述
题目中说 dis ⁡ ( x , y ) \operatorname{dis}(x, y) dis(x,y) x , y x,y x,y 两点之间唯一简单路径上边权的异或和。因此:
dis ⁡ ( x , y ) = dis ⁡ ( x , i ) ⊕ dis ⁡ ( y , i ) \operatorname{dis}(x,y) = \operatorname{dis}(x,i)\oplus \operatorname{dis}(y,i) dis(x,y)=dis(x,i)dis(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) valx,y(l)valx,y(l+1)valx,y(r1)valx,y(r)
= ( dis ⁡ ( x , l ) ⊕ dis ⁡ ( y , l ) ) ⊕ ( dis ⁡ ( x , l + 1 ) ⊕ dis ⁡ ( y , l + 1 ) ) ⊕ ⋯ ⊕ ( dis ⁡ ( x , r − 1 ) ⊕ dis ⁡ ( y , r − 1 ) ) ⊕ ( dis ⁡ ( x , r ) ⊕ dis ⁡ ( y , r ) ) = (\operatorname{dis}(x,l)\oplus \operatorname{dis}(y,l))\oplus (\operatorname{dis}(x,l + 1)\oplus \operatorname{dis}(y,l + 1)) \oplus \cdots \oplus(\operatorname{dis}(x,r - 1)\oplus \operatorname{dis}(y,r - 1))\oplus (\operatorname{dis}(x,r)\oplus \operatorname{dis}(y,r)) =(dis(x,l)dis(y,l))(dis(x,l+1)dis(y,l+1))(dis(x,r1)dis(y,r1))(dis(x,r)dis(y,r))
= dis ⁡ ( x , y ) ⊕ dis ⁡ ( x , y ) ⊕ ⋯ ⊕ dis ⁡ ( x , y ) =\operatorname{dis}(x,y)\oplus \operatorname{dis}(x,y) \oplus \cdots \oplus\operatorname{dis}(x,y) =dis(x,y)dis(x,y)dis(x,y)
即, r − l + 1 r - l + 1 rl+1 dis ⁡ ( x , y ) \operatorname{dis}(x,y) dis(x,y)进行异或。由前面的性质可以知道:

  • 如果 r − l + 1 r - l + 1 rl+1是奇数,则原式 = dis ⁡ ( x , y ) =\operatorname{dis}(x,y) =dis(x,y)
  • 如果 r − l + 1 r - l + 1 rl+1是偶数,则原式 = 0 =0 =0

可见本题变为了求 dis ⁡ ( x , y ) \operatorname{dis}(x,y) dis(x,y),如果用遍历求任意两点的 dis ⁡ ( x , y ) \operatorname{dis}(x,y) dis(x,y)就会超时,所以可以转化为 dis ⁡ ( x , y ) = dis ⁡ ( x , 1 ) ⊕ dis ⁡ ( y , 1 ) , \operatorname{dis}(x,y)=\operatorname{dis}(x,1)\oplus\operatorname{dis}(y,1), dis(x,y)=dis(x,1)dis(y,1)因此只需要遍历求出1号点到任意点的距离,可以用堆优化的Dijkstra或者DFS来完成


代码

方法1(Dijkstra堆优化版) 时间复杂度: O ( n × l o g ( n ) + q ) O(n \times log(n) + q) O(n×log(n)+q)

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

typedef pair<int, int> PII;
const int N = 2e6 + 10;

int n, q;
int h[N], e[N], w[N], ne[N], idx;
int l, r, x, y;
int g[N];
int dist[N], st[N];

inline int read(){
	int sum = 0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch^48),ch=getchar();
	return sum;
}

inline void write(int x){
	if(x/10) write(x/10);
	putchar(x%10^48);
}

inline void add(int a, int b, int c)  // 添加一条边a->b,边权为c
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

inline void dijkstra()  // 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
{
   memset(dist, 0x3f, sizeof dist);
   memset(st, 0, sizeof st);

   priority_queue<PII, vector<PII>, greater<PII>> heap;

   dist[1] = 0;
   heap.push({0, 1});

   while (heap.size())
   {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, dis = t.first;

        if (st[ver]) continue;
        st[ver] = 1;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
        	if (w[i] != -1)
        	{
            	dist[e[i]] = w[i] ^ dis;
            	heap.push({dist[e[i]], e[i]});
            }
        }
   }
}

int main()
{
	cin.tie(0);
	ios::sync_with_stdio(0);
	
	memset(h, -1, sizeof h);
	memset(w, -1, sizeof w);
	
	n = read(), q = read();
	
	int a, b, c;
	for (int i = 1; i < n; i ++)
		a = read(), b = read(), c = read(), add(a, b, c), add(b, a, c);
		
	dijkstra();
	
	for (int i = 1; i <= n; i ++)
		g[i] = dist[i];
		
	while (q --)
	{
		x = read(), y = read(), l = read(), r = read();
		
		write(bool(r - l + 1 & 1) * (g[x] ^ g[y]));
		
		putchar('\n');
	}

	return 0;
}

注:本题如果用该方法算时间卡的比较死,需要快读,否则会 T L E TLE TLE


方法2(DFS) 时间复杂度: O ( n + q ) O(n + q) O(n+q)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+7;
struct edge{
	int nxt,v,w;
}e[N<<1];
int h[N],cnt;
inline void add_edge(int u,int v,int w){
	e[++cnt].nxt=h[u],e[cnt].v=v,e[cnt].w=w;
	h[u]=cnt;
}
int n,q;
int x,y,l,r;
int dis[N];
inline void dfs(int u,int fa){
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v,w=e[i].w;
		if(v==fa) continue;
		dis[v]=dis[u]^w;
		dfs(v,u);
	}
}
signed main(){
    cin.tie(0);
    ios::sync_with_stdio(0);
    
	cin >> n >> q;
	for(int i=1;i<n;i++){
		int u, v, w;
		cin >> u >> v >> w;
		add_edge(u,v,w),add_edge(v,u,w);
	}
	dfs(1,0);
	for(int i=1;i<=q;i++){
		cin >> x >> y >> l >> r;
		cout << (r-l+1&1?dis[x]^dis[y]:0) << endl;
	}
	return 0;
}

注:此方法参考大佬AC_CSP的题解~~~


今天就到这里了!

大家有什么问题尽管提,我都会尽力回答的!最后,大年初二祝大家新年快乐!
在这里插入图片描述

吾欲您伸手,点的小赞赞。吾欲您喜欢,点得小关注!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值