CF1149E Election Promises(博弈论+构造)

题目:洛谷CF1149ECF1149E

题目描述:

给定一个\(n\)个点、\(m\)条边的有向无环图(\(DAG\)),每个点上都有权值

一共有两个人,两个人轮流,一个人每次可以选择任意一个点上权值大于\(0\)的点,将这个点上的权值降低为一个非负整数,并可以将这个点指向的点赋为任意非负整数

当一个人不能操作时,他就输了

判断先手是否必胜,如果先手必胜,输出先手第一步的任意一个必胜的走法

\(n,m \leq 200000\)

蒟蒻题解:

在一个先手必输的局面,先手选择一个点,降低了那个点的权值,并对这个点指向的点任意赋值,由于原局面先手必输,所以后手能够通过某种操作使得操作后的局面与先手操作前的局面等价

考虑对点集分组,一个组的权值定义为组内所有权值的异或和

在这种分组下,先手操作后会修改若干组的权值,后手再次操作可以使得各组的权值恢复原状

尝试用博弈论常用的\(mex\)去构造,令每个点所在的组为它所指向的点所在组的\(mex\),那么先手会修改了某些组的权值,后手想要把这些修改后的权值修改回去

考虑什么情况下后手一定能把修改后的权值修改回去,当且仅当被修改的最大的组的权值原先为\(0\)

当最大的组的权值原先为\(0\)时,先手将其修改掉,改成了一个非\(0\)的数,这个非\(0\)的权值是由这个组的点集的权值异或而成,所以肯定可以把它变为\(0\)

当最大的组的权值原先不为\(0\)时,会存在某种局面使得后手不一定能修改回去,且如果先手将这个值改为\(0\)的话,后手将其改为非\(0\),先手一定还有方法将其改为\(0\),所以这种情况可能会导致先手必胜

又最后的局面是所有组的权值全为\(0\),所以后手必胜的局面与最后的局面等价,即所有的组的权值为\(0\)

当存在有某个组权值不为\(0\)时,可以选择最大的权值不为\(0\)的组,将这个组的权值修改为\(0\),并且由于每个点所在的组为其指向的点所在组的\(mex\),所以它能将小于它的组的权值修改为任意非负整数,故可以将这个局面转化为所有组的权值全为\(0\)

故当初始局面所有组的权值均为\(0\)时后手必胜,否则先手必胜,当先手必胜就是要找到将所有组的权值变为\(0\)的一种方法即可

时间复杂度为\(\mathcal O(n+m)\)

参考程序:

#include<bits/stdc++.h>
using namespace std;
#define Re register int

const int N = 200005;
int n, m, cnt, l = 1, r, mx, s, h[N], hea[N], nxt[N], to[N], hea1[N], nxt1[N], to1[N], d[N], q[N], vis[N], g[N], f[N];

inline int read()
{
	char c = getchar();
	int ans = 0;
	while (c < 48 || c > 57) c = getchar();
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return ans;
}

inline void write(int x)
{
	int num = 0;
	char sc[15];
	if (!x) sc[num = 1] = 48;
	while (x) sc[++num] = x % 10 + 48, x /= 10;
	while (num) putchar(sc[num--]);
	putchar(' ');
}

inline void add(int x, int y)
{
	nxt[++cnt] = hea[x], to[cnt] = y, hea[x] = cnt;
	nxt1[cnt] = hea1[y], to1[cnt] = x, hea1[y] = cnt, ++d[x];
}

int main()
{
	n = read(), m = read();
	for (Re i = 1; i <= n; ++i) h[i] = read();
	for (Re i = 0; i < m; ++i)
	{
		int u = read(), v = read();
		add(u, v);
	}
	for (Re i = 1; i <= n; ++i)
		if (!d[i]) q[++r] = i;
	while (l <= r)
	{
		int x = q[l++];
		for (Re i = hea[x]; i; i = nxt[i]) vis[g[to[i]]] = x;
		while (vis[g[x]] == x) ++g[x];
		if (g[x] > mx) mx = g[x];
		f[g[x]] ^= h[x];
		for (Re i = hea1[x]; i; i = nxt1[i])
		{
			int u = to1[i];
			if (!(--d[u])) q[++r] = u;
		}
	}
	for (Re i = mx; i >= 0; --i)
		if (f[i])
		{
			s = i + 1;
			break;
		}
	puts(s ? "WIN" : "LOSE");
	if (!s) return 0;
	--s;
	for (Re i = 1; i <= n; ++i)
		if (g[i] == s && (h[i] ^ f[s]) < h[i])
		{
			h[i] ^= f[s], s = i;
			break;
		}
	for (Re i = hea[s]; i; i = nxt[i])
	{
		int u = to[i];
		if (f[g[u]]) h[u] ^= f[g[u]], f[g[u]] = 0;
	}
	for (Re i = 1; i <= n; ++i) write(h[i]);
	return 0;
}
posted @ 2021-05-18 16:56  clfzs  阅读(127)  评论(1)    收藏  举报