最小生成树变形,经典
题目意思:
有n个点,m条边。 然后问,求其中一个生成树,使得这个树的每一条边长度 全部 进行 与 运算,得到的结果最大。
其中,边长度 <= 1e9, 长度在 int 的范围。
本题要点:
1、int 有 32 位。
vector v[32]; // v[k] 存放的是边权 的第k位二进制是 1 的边的编号。
2、从最高位 k 开始, 如果 v[k].size() >= n - 1, 说明有 n - 1 条边,可能够成一个生成树,使得每一条边 进行 与
运算后,其值不为0; 容器 v[k] 里面装的所有边,进行最小生成树 kruskal 算法计算,看看是否能构成生成树。
3、 用一个 数组 vis[MaxM] 来统计有效边。
有效边,指的是, 从最高位开始 扫描, 存在一组 v[k] 里面的所有边,使得 kruskal 返回 true; 然后, 容器 v[k]
里面的所有边就是有效边。 当存在下一组 v[k1] ,也使得 kruskal 返回 true, 此时要更新有效边。
更新方法,就是在之前的有效边中,寻找该边权值的第 k1 位是 1 的边 新设置为 true,其他边设为 false;
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int MaxN = 300010, MaxM = 300010;
vector<int> v[32];
bool vis[MaxM];
int T, n, m;
int fa[MaxN];
int tmp[MaxN], cnt;
bool is_first;
struct res
{
int x, y, z;
}edge[MaxM];
int get(int x)
{
if(x == fa[x])
return x;
return fa[x] = get(fa[x]);
}
bool kruskal(int i)
{
int len = v[i].size(), x, y;
cnt = 0;
for(int k = 0; k < len; ++k)
{
if(is_first || vis[v[i][k]])
{
tmp[cnt++] = v[i][k];// tmp 放的是边的编号
}
}
if(cnt == 0) return false;
for(int k = 0; k < cnt; ++k)
{
x = edge[tmp[k]].x, y = edge[tmp[k]].y;
fa[x] = x, fa[y] = y; //初始化
}
int add_edge = 0;
for(int k = 0; k < cnt; ++k)
{
x = edge[tmp[k]].x, y = edge[tmp[k]].y;
int fax = get(x), fay = get(y);
if(fax == fay) continue;
++add_edge;
fa[fax] = fay;
}
return add_edge >= n - 1;
}
void solve()
{
is_first = true;
for(int i = 0; i <= m; ++i)
vis[i] = false;
int ans = 0;
for(int i = 31; i >= 0; --i)
{
if((int)v[i].size() < n - 1) continue;
if(kruskal(i))
{
int len = v[i].size();
if(is_first)
{
is_first = false;
for(int k = 0; k < len; ++k)
{
vis[v[i][k]] = true;
}
}else{
int r = 1 << i;
for(int k = 0; k < len; ++k)
{
if((edge[v[i][k]].z & r) == 0)
{
vis[v[i][k]] = false;
}
}
}
ans += 1 << i; //答案累加
}
}
printf("%d\n", ans);
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i)
{
scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].z);
for(int j = 0; j < 32; ++j)
{
int r = 1 << j;
if(edge[i].z & r)
v[j].push_back(i); // v存边的编号
}
}
solve();
}
return 0;
}
/*
1
4 5
1 2 5
1 3 3
1 4 2
2 3 1
3 4 7
*/
/*
1
*/