题意:一个长度为 n 的数组 a,有 q 个条件,每个条件有 3 个整数 i,j,x,表示 ai | aj = x。找出满足条件的最小字典序的数组 a。
思路:用二进制看每个数,通过 ai | aj = x,我们不能确定第 i 个数和第 j 个数哪一位是 1,但能确定哪些位一定是 0,所以我们可以求出每个数的最大值是和这个位置有关的 x 全部 & 在一起,我们用一个数组 f 来维护每个数最多能贡献多少。然后从小到大遍历数组 a,每次遍历确定一个数,假设当前遍历到第 i 个数,再遍历所有与 i 有关的 j 和 x,如果 j > i,那么尽量要让 aj 来贡献出 x,aj 可以贡献的部分是 f j & x,不能贡献的部分是 ~f j & x,那么这部分就需要 ai 来提供,如果 j < i,aj已经是确定的最小值,所以 aj 不能贡献的部分为 ~ aj & x, 这部分同样需要 ai 来提供,所以 ai 的值就是把这些需要 ai 来提供的部分 | 在一起。
代码:
#include<bits/stdc++.h>
#define pb push_back
#define all(x) x.begin(), x.end()
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10, P = 1e9 + 7, mod = 998244353;
void solve(){
int n, q;
cin >> n >> q;
vector<int> f(n + 1, (1 << 30) - 1);
vector<PII> v[n + 1];
for(int i = 1; i <= q; i++) {
int a, b, c;
cin >> a >> b >> c;
f[a] &= c, f[b] &= c;
v[a].pb({b, c});
v[b].pb({a, c});
}
for(int i = 1; i <= n; i++){
int t = 0;
for(auto p : v[i]) {
int j = p.first, x = p.second;
t |= x & ~f[j];
if(j == i) t = x;
}
f[i] = t;
cout << t << " ";
}
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int tt;
//cin >> tt;
//while(tt--) {
solve();
//}
return 0;
}