Minimum Or Spanning Tree
题目描述:
n
个点,m
条边,无向图,求边权按位或后得到的最小生成树的值
思路:
考虑按位贪心
显然高位能不选就不选,所以我们可以从高位
i
开始枚举,对于每一位都跑一次最小生成树,看在当前边集的基础上,不选第i
位为1的边能不能满足是一颗最小生成树,如果可以的话说明第i
位确实可以不选,我们就把当前边集中第i
位是1的边扣掉,如果不能构成一颗最小生成树的话,说明这一位必须选,所以我们就更新一下答案,边集不变
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
typedef pair <int,int> pii;
#define MAX 500000 + 50
int n, m, k, x;
struct ran{
int u, v, c;
}tr[MAX];
bool vis[MAX];
int fa[MAX];
int getfa(int x){
return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
void emerge(int x, int y){
fa[getfa(x)] = getfa(y);
}
void work(){
cin >> n >> m;
for(int i = 1; i <= m; ++i){
cin >> tr[i].u >> tr[i].v >> tr[i].c;
}
int ans = 0;
for(int j = 30; j >= 0; --j){
for(int i = 1; i <= n; ++i)fa[i] = i;
for(int i = 1; i <= m; ++i){
if(vis[i] || ((tr[i].c>>j)&1))continue;
emerge(tr[i].u, tr[i].v);
}
bool p = 1;
for(int i = 1; i <= n; ++i){
if(getfa(i) != getfa(1))p = 0;
}
if(p){
for(int i = 1; i <= m; ++i){
if((tr[i].c>>j)&1)vis[i] = 1;
}
}
else ans += (1<<j);
}
cout << ans << endl;
}
int main(){
io;
work();
return 0;
}