cf1149E. Election Promises
题解
有向无环图的博弈游戏有个小技巧就是把点分组,每个点 $u$ 所在的集合 $M(u)=\text{mex}\{M(v)|(u,v)\in G\}$
然后如果每个组的异或值为 $0$ 的话,那么就是先手必败,否则先手必胜。
因为我们可以考虑找到异或值不为 $0$ 的最大编号的组,然后把其中一个数 $x$ 减小至这个组异或值为 $0$ ,然后对于 $x$ 的出边所指的点 $v$ ,把 $v$ 点改为使得其组异或值为 $0$ 的值。这样就是每个组异或值都为 $0$ 的情况了。因此先手必胜。
求 $\text{mex}$ 只要 $\text{bfs}$ 一遍就好了,效率: $O(n+m)$ 。
代码
#include <bits/stdc++.h> using namespace std; const int N=2e5+5; int n,m,a[N],hd[N],V[N],nx[N],t,g[N],p[N],s[N],in[N]; vector<int>e[N]; queue<int>q; void add(int u,int v){ nx[++t]=hd[u];V[hd[u]=t]=v; } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v), add(v,u),in[u]++, e[u].push_back(v); for (int i=1;i<=n;i++) if (!in[i]) q.push(i); for (;!q.empty();){ int u=q.front(),z;q.pop(); for (int i=hd[u];i;i=nx[i]) if (!(--in[V[i]])) q.push(V[i]); z=e[u].size(); for (int i=0;i<z;i++) p[g[e[u][i]]]=u; for (int i=0;;i++) if (p[i]!=u){ g[u]=i;s[i]^=a[u];break; } } for (int i=n;~i;i--){ if (!s[i]) continue; puts("WIN"); for (int j=1,z;j<=n;j++) if (g[j]==i && (s[i]^a[j])<a[j]){ a[j]=(s[i]^a[j]); z=e[j].size(); for (int v,k=0;k<z;k++) v=e[j][k],a[v]=s[g[v]]^a[v],s[g[v]]=0; break; } for (int j=1;j<=n;j++) printf("%d%c",a[j],j<n?' ':'\n'); return 0; } puts("LOSE"); return 0; }