题意:
给一副连通无向图,求最小或生成树的权值,或位二进制的或。
方法:
按位贪心,我们要求最小权值,自然不想让1出现在结果的高位上,那么我们从高位到低位考虑,设我们考虑到了第 i i i位,如果我们能不用第 i i i位是1的所有边来构造一颗生成树的话,显然我们是一定不考虑第 i i i位是1的边的,因为我们不想让1出现在高位,尽可能出现在更低位。所以我们我们从高到低来遍历位,如果这一位可以摒弃,那么我们对所有包含这一位的边打上标记,我们一定不使用他们,否则我们就不可避免地使用其中一条边,所以这一位就一定是1,就让 a n s ∣ = 1 < < i ans|=1<<i ans∣=1<<i
判断能否构成生成树利用并查集即可
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
struct DSU
{
vector<int>f;
DSU(){};
DSU(int n){
f.resize(n+1);
for(int i=1;i<=n;i++) f[i]=i;
}
int find(int k)
{
if(f[k]==k) return k;
return f[k]=find(f[k]);
}
void merge(int x,int y)
{
if(x==y) return;
x=find(x);y=find(y);
f[x]=y;
}
bool same(int x,int y){
return find(x)==find(y);
}
int operator[](int k){
return f[k]=find(f[k]);
}
};
struct way
{
int u,v,w;
}edge[400005];
int n,m;
bool unused[400005];
bool check(int k)
{
//检查是否不靠pos位就能构造生成树
DSU x(n);
int cnt=0;
for(int i=1;i<=m&&cnt<n-1;i++)
{
int u=edge[i].u,v=edge[i].v,w=edge[i].w;
if(unused[i]||((w>>k)&1)) continue;
if(x.same(u,v)) continue;
x.merge(u,v); cnt++;
}
return cnt==n-1;
}
void tag(int k)
{
for(int i=1;i<=m;i++){
if(edge[i].w>>k&1) unused[i]=true;
}
}
void work()
{
cin>>n>>m;
int ans=0;
for(int i=1;i<=m;i++) unused[i]=false;
for(int i=1;i<=m;i++) cin>>edge[i].u>>edge[i].v>>edge[i].w;
for(int i=30;i>=0;i--)
{
//检查不靠这一位能否组成一颗生成树
//也就是检查这一位为0的所有边,如果是,那么标记上,我们就不用这个位置为1的所有边
if(check(i)) tag(i);
else ans|=1<<i;
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int t=1;cin>>t;
while(t--) work();
return 0;
}