其他分享
首页 > 其他分享> > CF #816 D - 2+ doors

CF #816 D - 2+ doors

作者:互联网

建图,贪心

Problem - D - Codeforces

题意

给 q 个约束 \(a[i] \;|\;a[j]==x\), 求满足这 q 个约束的最小字典序的数组 a (保证有解)

思路

  1. 把 q 个约束中 i == j 的优先处理掉,即 a[i] = x,这些位置不再考虑

  2. 按位考虑,分别处理 30 位

  3. 对于第 k 位,把 \(a[i] \;|\;a[j]==x\) 中 x 在第 k 位为 0 的先处理掉,因为此时 a[i], a[j] 的第 k 位只能为 0;

    其他的位置都赋 1,这样就足够满足所有约束了

  4. 接下来要找到字典序最小的,就贪心地让下标从小到大尝试,看是否能让 a[i] 的第 k 位为 0

  5. 可以把约束中的两个下标连边,表示一条边中至少有个点为 1,然后从小到达枚举 u,再枚举 u 的邻点 v,如果所有的 v 此时都为 1,那么 u 就可以为 0;否则 u 只能为 1

  6. 注意这样会建 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