1149E

博弈论

如果没有修改后继城市这个操作,就是一个$nim$游戏

现在将整个图的$sg$函数求出 把所有点按$sg$函数分类 设一类点的编号为$sg$函数值 每一类点有以下性质

1.这类点之间互相没有直接连边

2.这类点能到达所有编号小于该类点的集合中的至少一点

以上根据$sg$函数性质可以直接推出

设每类点的$sum$为所有$h$的异或和 根据$nim$游戏 如果所有类的$sum$都为$0$ 那么先手必输 因为如果先手修改某个点的$h$以及后继 那么同类点中肯定存在某个点可以通过同样的操作使得$sum$重新变回$0$ 直到不可操作

如果$sum$存在不为$0$ 那么先手必胜 找到编号最大的那类点中某点 修改该点及后继点的$h$使得$sum$全部为$0$即可

1149E
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int n, m;
int h[maxn], in[maxn], q[maxn], sg[maxn], sum[maxn], vis[maxn];
vector<int> G[maxn];
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i) scanf("%d", &h[i]);
    for(int i = 1; i <= m; ++i) {
        int u, v; scanf("%d%d", &u, &v);
        G[u].push_back(v);
        ++in[v];
    }
    int l = 1, r = 0;
    for(int i = 1; i <= n; ++i) if(!in[i]) q[++r] = i;
    while(l <= r) {
        int u = q[l++];
        for(int i = 0; i < G[u].size(); ++i) {
            int v = G[u][i];
            if(!--in[v]) q[++r] = v;
        }
    }
    for(int i = n; i; --i) {
        int u = q[i];
        for(int j = 0; j < G[u].size(); ++j) {
            int v = G[u][j];
            vis[sg[v]] = 1;
        }
        for(; vis[sg[u]]; ++sg[u]);
        for(int j = 0; j < G[u].size(); ++j) {
            int v = G[u][j];
            vis[sg[v]] = 0;
        }
    }
    for(int i = 1; i <= n; ++i) sum[sg[i]] ^= h[i];
    int p = -1;
    for(int i = n; ~i; --i) if(sum[i]) {
        p = i;
        break;
    } 
    if(p == -1) {
        puts("LOSE");
        return 0;
    }
    puts("WIN");
    for(int i = 1; i <= n; ++i) if(sg[i] == p) {
        if((sum[p] ^ h[i]) > h[i]) continue;
        h[i] ^= sum[p]; 
        for(int j = 0; j < G[i].size(); ++j) {
            int v = G[i][j];
            h[v] ^= sum[sg[v]];
            sum[sg[v]] = 0;
        }
        break;
    }
    for(int i = 1; i <= n; ++i) printf("%d ", h[i]);
    return 0;
} 
View Code

 

上一篇:SG函数的理解集应用


下一篇:WaWa的奇妙冒险(第七周集训自闭现场)