个人认为这道 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;
}
标签:100010,int,题解,一位,枚举,CF1715D,doors,ans,相连
From: https://www.cnblogs.com/Dregen-Yor/p/16613000.html