G. SlavicG‘s Favorite Problem(树的遍历DFS,BFS均可)

Problem - G - Codeforces

给你一棵有n个顶点的加权树。回顾一下,树是一个没有任何循环的连接图。加权树是一棵树,其中每条边都有一定的权重。这棵树是无定向的,它没有根。

由于树让你感到厌烦,你决定挑战自己,在给定的树上玩一个游戏。

在一次移动中,你可以从一个节点移动到它的一个邻居(与它有直接边的另一个节点)。

当你通过边i时,x的值变为x XOR wi(其中wi是第i条边的权重)。

你的任务是从顶点a到顶点b,但你被允许进入节点b,当且仅当旅行到它之后,x的值将变成0。换句话说,你只能通过使用边i,使x XOR wi=0来旅行到节点b。

此外,你可以在任何时间点最多传送一次到任何顶点,除了顶点b。你可以从任何顶点传送,甚至从a点传送。

如果你能从a到达顶点b,则回答 "是",否则回答 "否"。

请注意,XOR代表位XOR操作。

输入
第一行包含一个整数t(1≤t≤1000)--测试案例的数量。

每个测试案例的第一行包含三个整数n、a和b(2≤n≤105),(1≤a,b≤n;a≠b)--分别是顶点的数量,以及起始节点和期望的结束节点。

接下来的n-1行中的每一行表示树的一条边。边缘i由三个整数ui、vi和wi表示--它连接的顶点的标签(1≤ui,vi≤n;ui≠vi;1≤wi≤109)和各自边缘的权重。

保证所有测试案例的n之和不超过105。

输出
对于每个测试案例,如果你能到达顶点b,则输出 "YES",否则输出 "NO"。

例子
输入复制
3
5 1 4
1 3 1
2 3 2
4 3 3
3 5 1
2 1 2
1 2 2
6 2 3
1 2 1
2 3 1
3 4 1
4 5 3
5 6 5
outputCopy

没有

注意
对于第一个测试案例,我们可以从节点1到节点3,x从0变成1,然后我们从节点3到节点2,x变成等于3。现在,我们可以传送到节点3,从节点3到节点4,到达节点b,因为x最后变成等于0,所以我们应该回答 "是"。

对于第二个测试案例,我们没有移动,因为我们不能传送到节点b,我们唯一的移动是前往节点2,这是不可能的,因为到达节点2时x不会等于0,所以我们应该回答 "不"。

题解:
我们必须搞清楚一点当你旅行到b后,x的值是必须为0的,也就是说走到b后就要停下了(很重要)

a可以通过传送达到除b外的任意一点,并且每经过一条路径就要做^操作,

我们不妨双向考虑,从起点与终点出发,如果a遍历到某一点的值,与b遍历到某一点的值相等,那么是不是可以让a直接传送到b遍历到的点,众所周知,两个相等的值^结果为0,最终到达b点的值也会为0

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<vector>
#include<queue>
using namespace std;
vector<pair<int,int>> p[100050];
int n,a,b;
int f = 0;
int dis[100050];
int vis[100050];
int dis1[100050];
int vis1[100050];
map<int,int> rr,ll;
void bfs()
{
	queue<int> q;
	q.push(a);
	dis[a] = 0;
	while(q.size())
	{
		auto ver = q.front();
		q.pop();
		if(vis[ver])
		continue;
		vis[ver] = 1;
		for(int i = 0;i < p[ver].size();i++)
		{
			auto j = p[ver][i];
			int ne = j.first;
			int ww = j.second;
			if(!vis[ne])
			{
				if(ne != b)
				{
				dis[ne] = ww^dis[ver];
				q.push(ne);
				}
			}
		}
	}
}

void bfs1()
{
	queue<int> q;
	q.push(b);
	dis1[b] = 0;
	while(q.size())
	{
		auto ver = q.front();
		q.pop();
		if(vis1[ver])
		continue;
		vis1[ver] = 1;
		for(int i = 0;i < p[ver].size();i++)
		{
			auto j = p[ver][i];
			int ne = j.first;
			int ww = j.second;
			if(!vis1[ne])
			{
				dis1[ne] = ww^dis1[ver];
				rr[dis1[ne]] = 1;
				q.push(ne);
			}
		}
	}
}
void solve()
{
	f = 0;
	cin >> n >> a >>b;
	rr.clear();
	ll.clear();
	for(int i = 1;i <= n;i++)
	{
		p[i].clear();
		vis[i] = 0;
		dis[i] = 0;
		vis1[i] = 0;
		dis1[i] = 0;
	}
	for(int i = 1;i < n;i++)
	{
		int l,r,w;
		cin >> l>>r>>w;
		p[l].push_back({r,w});
		p[r].push_back({l,w});
	}
	bfs();
	bfs1();
	for(int i = 1;i <= n;i++)
	{
		if(rr[dis[i]])
		{
			cout<<"YES\n";
			return ;
		}
	}
	cout<<"NO\n";
}
signed main()
{
	int t = 1;
	cin >> t;
	while(t--)
	{
		solve();
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值