CF #816 D - 2+ doors
作者:互联网
建图,贪心
题意
给 q 个约束 \(a[i] \;|\;a[j]==x\), 求满足这 q 个约束的最小字典序的数组 a (保证有解)
思路
-
把 q 个约束中 i == j 的优先处理掉,即 a[i] = x,这些位置不再考虑
-
按位考虑,分别处理 30 位
-
对于第 k 位,把 \(a[i] \;|\;a[j]==x\) 中 x 在第 k 位为 0 的先处理掉,因为此时 a[i], a[j] 的第 k 位只能为 0;
其他的位置都赋 1,这样就足够满足所有约束了
-
接下来要找到字典序最小的,就贪心地让下标从小到大尝试,看是否能让 a[i] 的第 k 位为 0
-
可以把约束中的两个下标连边,表示一条边中至少有个点为 1,然后从小到达枚举 u,再枚举 u 的邻点 v,如果所有的 v 此时都为 1,那么 u 就可以为 0;否则 u 只能为 1
-
注意这样会建 30 个图,实际上建一个图就好,用 w >> k & 1 取出第 k 位时的边
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
#define endl "\n"
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n, q;
struct Edge
{
int v, w;
};
vector<vector<Edge> > G(N);
int a[N];
bool st[N];
void add(int u, int v, int w)
{
G[u].push_back({v, w});
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; i++)
a[i] = (1 << 30) - 1;
while(q--)
{
int u, v, w;
cin >> u >> v >> w;
if (u == v)
{
a[u] = w;
st[u] = true;
continue;
}
add(u, v, w), add(v, u, w);
for (int k = 0; k < 30; k++)
{
if (!(w >> k & 1))
{
if (a[u] >> k & 1)
a[u] ^= (1 << k);
if (a[v] >> k & 1)
a[v] ^= (1 << k);
}
}
}
for (int k = 0; k < 30; k++)
{
for (int u = 1; u <= n; u++)
{
if (st[u] || !(a[u] >> k & 1))
continue;
bool fl = true;
for (auto [v, w] : G[u])
{
if (!(w >> k & 1))
continue;
if (!(a[v] >> k & 1))
{
fl = false;
break;
}
}
if (fl)
a[u] ^= (1 << k);
}
}
for (int i = 1; i <= n; i++)
cout << a[i] << " ";
cout << endl;
return 0;
}
标签:int,30,CF,add,continue,doors,include,fl,816 来源: https://www.cnblogs.com/hzy717zsy/p/16636643.html