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;
}

 

posted @ 2021-11-10 19:15  xjqxjq  阅读(23)  评论(0编辑  收藏  举报