CF1715D 2+ doors 题解

个人认为这道 D 比 C 要简单

思路

因为题目中每个条件限制为$ a_i \mid a_j = x $,并且题目中还提到 \(x<2^{30}\),我们考虑将 \(x\) 转换成二进制的方式表示,枚举 \(x\) 的每一位,若枚举到的当前位置上为 \(0\),则 \(a_i\)\(a_j\) 上的该位不能赋值成 \(1\),用数组标记一下。

对于每一个关系,我们可以以图的形式来表示,每一个 $ a_i \mid a_j = x $ 的关系,可以在 \(i\)\(j\) 中间连一条权值为 \(x\) 的边。在遍历的时候,对于每一条边,为了保证字典序最小,我们可以尽可能地让 \(i\)\(j\) 中较大的一位尽可能地和 \(x\) 的每一位相等。

我们可以枚举二进制的每一位,枚举每一位时,枚举每个点和当前的点相连的边,若相连的点的下标比当前的小且边权中包含这一位,同时相连的点的答案中不包含这一位,就将当前点的答案的这一位变成 \(1\),如果相连的点的下标比当前的大且边权中包含这一位,若相连的点的这一位被标记,将当前点的答案的这一位变为 \(1\)

最终输出答案即可。

代码

#include <bits/stdc++.h>
using namespace std;

struct node {
	int to, k;
};
vector <node> G[100010];
int n, m, v[100010][31], ans[100010];

int main() {
	scanf("%d%d", &n, &m);
	memset(ans, 0, sizeof(ans));

	for (int i = 1; i <= m; i++) {

		int x, y, k;
		scanf("%d%d%d", &x, &y, &k);

		for (int j = 0; j < 30; j++) {

			if (k & (1 << j)) {
				continue;
			}

			v[x][j] = v[y][j] = 1;
		}

		if (x == y) {
			ans[x] |= k;
			continue;
		}

		G[x].push_back(node{y, k});
		G[y].push_back(node{x, k});
	}

	for (int x = 29; x >= 0; x--) {

		for (int i = 1; i <= n; ++i) {

			bool f = 0;

			for (int j = 0; j < G[i].size(); j++) {

				int to = G[i][j].to, k = G[i][j].k;

				if (to < i) {
					if (k & (1 << x)) {
						if (ans[to] & (1 << x)) {
							continue;
						}

						f = 1;
						break;
					}
				} else {
					if (k & (1 << x)) {
						if (v[to][x]) {
							f = 1;
							break;
						}
					}
				}
			}

			if (f)
				ans[i] |= (1 << x);
		}
	}

	for (int i = 1; i <= n; i++) {

		printf("%d ", ans[i]);
	}

	return 0;
}


posted @ 2022-08-22 15:39  Dregen_Yor  阅读(108)  评论(0)    收藏  举报