题目链接:https://codeforces.ml/contest/1760/problem/G
div4最接近ak的一场,这个题没有读明白。其实上一个题也应该要整理的,做法还是有些复杂。这个题我的写法和别人写的相比也复杂一些。尤其是在树的遍历操作上,开sign是容易出错的==
题意:大意是给出一个n个节点的树,你需要从a走到b。到b时,你需要异或b的值等于0,才能进入b。每个边权都有一个值,初始值为0 。
在途中,你可以选择在任意位置,使用一次转移到其他任意位置(只能一次)。求是否可以到达b?
解决方法:跑一遍正向dfs,获得所有边权后的异或值,然后反向跑一遍dfs,如果这个异或值曾经获得过那么就可以从曾经的位置转移过来,然后正向异或还一遍正好为0.
涉及的数学规律,异或运算
0 5 6 3 2互相异或 = 0 2 3 6 5 互相异或
结论,一个点异或到另一个点 等于 从另一个点异或到这个点
a^b = b^a a^b^c = c^b^a 等等
通过百度进一步研究,异或存在一定的结合律,在数一定的情况想先异或哪个数和后异或哪个数结果一样,可以参加加分结合律
同样的异或存在一定的规律,https://blog.csdn.net/a3630623/article/details/12371727
从1异或到n只需要一个O(1)的复杂度,同一样从a异或到b也只需要常数级别复杂度
本题code:
#include<bits/stdc++.h> using namespace std; #pragma GCC optimize(2) #define ll long long #define endl '\n' #define cyes cout<<"YES\n" #define cno cout<<"NO\n" #define mem(a) memset((a),0,sizeof (a)) const int mod7 = 1e9+7; const int mod9 = 998244353; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();} return x*f; } struct tdata{ int v; int w; }; const int nn = 1e5+10; vector<tdata> ve[nn]; bool sign[nn]; bool ok = false; map<int,bool> mp; int n,a,b; void dfs1(int cur,int num){ for(int i=0;i<ve[cur].size();i++){ tdata nxt = ve[cur][i]; if(!sign[nxt.v]&&nxt.v!=b){ mp[num^nxt.w]=true; sign[nxt.v] = true; dfs1(nxt.v,num^nxt.w); } } } void dfs2(int cur,int num){ if(ok) return; for(int i=0;i<ve[cur].size();i++){ tdata nxt = ve[cur][i]; if(!sign[nxt.v]){ if(mp[num^nxt.w]){ ok = true; return; } sign[nxt.v] = true; dfs2(nxt.v,num^nxt.w); } } } void solve() { cin>>n>>a>>b; mp.clear();ok = false; for(int i=1;i<=n;i++) ve[i].clear(); int x,y,z; for(int i=1;i<n;i++){ cin>>x>>y>>z; ve[x].push_back({y,z}); ve[y].push_back({x,z}); } mp[0] = true; mem(sign); sign[a] = true; dfs1(a,0); mem(sign); sign[b] = true; dfs2(b,0); if(ok) cyes; else cno; } int main() { //cin.ignore(numeric_limits<streamsize>::max(),'\n') ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); int t; cin>>t; while(t--) { solve(); } }