【代码源每日一题Div1】Minimum Or Spanning Tree「按位贪心+MST」

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_51216553/article/details/127761597