原题链接
思路
看到这道题,很明显就能发现这道题其实跟图论有关,将\(A\)数组看成一张无向图,每一个节点\(i\)的点权就是\(A_i\),每两个节点\(i\)和\(j\)之间的边权就是\(A_i \oplus A_j\)。而我们可以枚举答案的每一个比特位,用BFS(或DFS,作者这里用的是BFS)来维护每一个连通块(因为只有连通块中的节点会相互影响),如果任意一个连通块中的节点发生了矛盾,则直接输出\(-1\)(因为只要有一个比特位不符合,所有答案就会有错误),并将程序结束。否则如果所有的节点都满足条件,则将答案记录下来,并在最后输出。
CODE
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
int n,m,x[100010],y[100010],z[100010],zy[200010],tp[200010];
vector<pair<int,int> >vec[200010];
int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=1;i<=m;i++) {cin>>x[i]>>y[i]>>z[i]; //输入vec[x[i]].push_back(make_pair(y[i],z[i])); //建无向边vec[y[i]].push_back(make_pair(x[i],z[i]));}for(int i=0;i<=30;i++) //枚举每一个比特位,因为10的9次方小于2的31次方,所以只用枚举31位就够了{memset(tp,-1,sizeof(tp)); //初始化标记数组,因为比特位的两种情况分别为0和1,所以只能初始化为-1for(int j=1;j<=n;j++){if(tp[j]!=-1) continue; //如果在一个连通块中,则跳过这个节点vector<int> ls;queue<int> q;tp[j]=0;q.push(j);while(!q.empty()){int l=q.front();q.pop();ls.push_back(l);for(auto eg:vec[l]) //遍历每条边{int v=eg.first,w=eg.second;if(tp[v]==-1) //如果没有初始值{tp[v]=((w>>i)&1)^tp[l]; //赋初始值q.push(v); //BFS}else {if(tp[v]!=((w>>i)&1)^tp[l]) //不符合条件{cout<<-1; return 0; //结束程序}}}}int cnt=0;for(auto v:ls) if(tp[v]==1) cnt++;if(cnt>ls.size()-cnt) for(auto v:ls) tp[v]=1-tp[v]; //这样做是因为这个图是一个无向图,会有两种相反的答案,而题目要求A数组总和最小,所以我们要让比特位上的1最少才能使答案最小for(auto v:ls) if(tp[v]==1) zy[v]|=(1<<i); //更新答案}}for(int i=1;i<=n;i++) cout<<zy[i]<<' '; //输出答案return 0;
}