把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

CF1149E Election Promises

题面传送门

怎么这么牛逼的题。

首先我们发现如果都是单点,那么就是一个nim游戏,但是并没有这档部分分

发现这个东西和有向图游戏比较相似,所以可以考虑先当成有向图游戏求出每个点的sg值,然后将相同sg值的\(a_i\)xor在一起,如果都为\(0\)则先手必败,否则先手必胜。

这个东西为什么正确呢,我们考虑分成两种情况,说明必胜局面操作导致必败,必败局面操作导致必胜。

先说明必胜局面操作导致必败,显然任意操作一个点\(x\)\(sg_x\)都会变成不是\(0\)的值,则变成先手必败。

在必胜局面操作的时候,设\(f_i\)\(sg_x=i\)的所有\(a_x\)的xor和,则找到最大的\(f_i\),再找到某个\(a_j \operatorname{xor} f_i<a_j\),显然这样的\(a_j\)一定存在,则将这个点操作成\(0\),因为这个点\(sg_x=i\),则其后继一定存在\(sg=0,1,\dots,i-1\),所以所有\(f\)都能操作成\(0\),就说明了必胜可以操作成必败。

那么跑一遍toposort就可以求出每个点的sg值,时间复杂度\(O(n+m)\)

#include<bits/stdc++.h>
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=5e5+5,M=N*8+5,K=2e3+5,mod=1e9+7,Mod=mod-1;const db eps=1e-9;const ll INF=1e18+7;mt19937 rnd(time(0));
int n,m,x,y,sg[N],f[N],A[N],In[N];vector<int> S[N],G[N];queue<int> Q;
int main(){
	freopen("1.in","r",stdin);
	int i,j,h;scanf("%d%d",&n,&m);for(i=1;i<=n;i++) scanf("%d",&A[i]);while(m--) scanf("%d%d",&x,&y),S[y].PB(x),G[x].PB(y),In[x]++;
	for(i=1;i<=n;i++) !In[i]&&(Q.push(i),0);
	while(!Q.empty()){
		int x=Q.front();Q.pop();for(int i:G[x]) f[sg[i]]++;
		while(f[sg[x]]) sg[x]++;for(int i:G[x]) f[sg[i]]--;
		for(int i:S[x]) !--In[i]&&(Q.push(i),0);
	}
	for(i=1;i<=n;i++) f[sg[i]]^=A[i];int YAI=0;for(i=0;i<=n;i++) if(f[i]){puts("WIN");YAI=1;break;}if(!YAI) {puts("LOSE");return 0;}
	for(i=n;~i;i--) if(f[i]){
		for(j=1;j<=n;j++)if(sg[j]==i&&(A[j]^f[i])<A[j]){
			A[j]^=f[i];for(int d:G[j]) if(f[sg[d]]) A[d]^=f[sg[d]],f[sg[d]]=0;
			for(h=1;h<=n;h++) printf("%d ",A[h]);return 0;
		}
	}
}

同样的还有CF1451F,对这道题同样分析,发现是将行列和分成一组的xor值,也同样是因为行列和每个都能碰到。

posted @ 2023-02-03 16:33  275307894a  阅读(18)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end