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

 

  

 

posted @ 2018-02-24 00:17  cxhscst2  阅读(253)  评论(0编辑  收藏  举报