ECNU 3462 最小 OR 路径 (贪心 + 并查集)
题目链接 EOJ Monthly 2018.1 Problem F
先假设答案的每一位都是$1$,然后从高位开始,选出那些该位置上为$0$的所有边,并查集判断连通性。
如果$s$和$t$可以连通的话,那么该位置$0$,然后用刚刚选出来的这些边再继续做下去。
如果$s$和$t$不连通的话,那么不做任何操作,继续处理低位。
这样就可以保证答案一定是最小的。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define MP make_pair #define fi first #define se second typedef long long LL; const int N = 1e4 + 10; const int M = 1e6 + 10; struct node{ int x, y; LL z; void scan(){ scanf("%d%d%lld", &x, &y, &z);} } e[M]; int n, m, s, t; int x; int father[N]; vector <node> f[2]; LL ans; int getfather(int x){ return father[x] ? father[x] = getfather(father[x]) : x; } int main(){ scanf("%d%d", &n, &m); rep(i, 1, m) e[i].scan(); scanf("%d%d", &s, &t); ans = (1ll << 62) - 1; rep(i, 1, m){ int x = e[i].x, y = e[i].y; int fx = getfather(x), fy = getfather(y); if (fx ^ fy){ father[fx] = fy; } } if (getfather(s) != getfather(t)) return 0 * puts("-1"); x = 0; rep(i, 1, m) f[0].push_back(e[i]); dec(i, 61, 0){ memset(father, 0, sizeof father); f[x ^ 1].clear(); for (auto edge : f[x]){ LL val = edge.z; if (!((1ll << i) & val)){ f[x ^ 1].push_back(edge); } } for (auto edge : f[x ^ 1]){ int x = edge.x, y = edge.y; int fx = getfather(x), fy = getfather(y); if (fx ^ fy) father[fx] = fy; } int fs = getfather(s), ft = getfather(t); if (fs == ft){ ans ^= (1ll << i); x ^= 1; } } printf("%lld\n", ans); return 0; }