CF1149E Election Promises

一、题目

点此看题

二、解法

显然本题一定有终止态,感受一下就好了我不想证明。

本题大概是把 \(\tt nim\) 游戏放在 \(\tt DAG\) 上然后改了点游戏规则,我们还是主要从 \(xor\) 的角度思考。

首先把每个点按找 \(mex\) 分组,定义 \(a_u\) 为点 \(u\) 的组别:

\(a_u=mex\{a_v\ |\ (u,v)\in E\}\)

然后找稳态,也就是必胜方能维持必败方得到的状态。考虑对于组别 \(p\) 求出所有属于该组点的异或和 \(xor(p)\),那么稳态就是 \(\forall p,xor(p)=0\),我们猜测初始状态是稳态就先手必败,否则先手必胜。

我们只需要证明这两点,非稳态可以通过一步操作转到稳态,一步操作稳态只能转到非稳态:

  • 第一点,我们找到满足 \(xor(p)\not=0\) 的最大的 \(p\),找到其中 \(h_u\)\(xor(p)\) 最高位相同的一个点 \(u\),显然 \(h_u\) 是应该减少的,首先我们把 \(h_u\) 的最高位变成 \(0\),然后剩下的位就可以任取了,所以可以让 \(xor(p)\) 变成 \(0\);然后因为 \(mex\) 的性质,我们可以通过直连边改变任意编号 \(<p\) 的组别,所以可以让所有组的 \(xor(p)=0\)
  • 第二点,任取一个点 \(u\),那么改变它的 \(h_u\) 会使 \(xor(a_u)>0\),因为 \(mex\) 的性质也无法通过直连边把它改回来,所以稳态只能转到非稳态。

那么跑拓扑排序就可以判断是否先手必胜了,如果先手必胜按上面的方法操作到稳态即可。

三、总结

解决非常规博弈问题的重要方法:找稳态,有稳态就有必胜策略。

分组是解决一个点对另一个点有影响的重要方法,博弈问题一定要多应用 \(mex\)\(xor\) 这两个玄学玩意。

#include <cstdio>
#include <vector>
#include <iostream>
#include <queue>
using namespace std;
const int M = 200005;
int read()
{
    int x=0,f=1;char c;
    while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
int n,m,k,a[M],b[M],s[M],h[M],d[M],id[M];
vector<int> g[M];queue<int> q;
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
		h[i]=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		g[u].push_back(v);d[v]++;
	}
	for(int i=1;i<=n;i++)
		if(!d[i]) q.push(i);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		id[++k]=u;//tuopu order
		for(auto v:g[u])
		{
			d[v]--;
			if(!d[v]) q.push(v);
		}
	}
	for(int i=k;i>=1;i--)
	{
		int u=id[i];
		for(auto v:g[u]) b[a[v]]=1;
		for(int x=0;;x++) if(!b[x])
		{
			a[u]=x;
			break;
		}
		for(auto v:g[u]) b[a[v]]=0;
	}
	for(int i=1;i<=n;i++)
		s[a[i]]^=h[i];
	int pd=0,rt=0;
	for(int i=0;i<=n;i++)
		if(s[i]) pd=1;
	if(!pd)
	{
		puts("LOSE");
		return 0;
	}
	puts("WIN");
	for(int i=1;i<=n;i++) if(s[a[i]])
		if(a[i]>=a[rt] && (s[a[i]]^h[i])<h[i])
			rt=i;
	for(auto v:g[rt])
		if(s[a[v]]) h[v]=s[a[v]]^h[v],s[a[v]]=0;
	h[rt]=s[a[rt]]^h[rt];
	for(int i=1;i<=n;i++)
		printf("%d ",h[i]);
}
posted @ 2021-08-26 16:31  C202044zxy  阅读(47)  评论(0编辑  收藏  举报