1 条题解

  • 0
    @ 2026-10-26 17:31:11

    思路与算法

    按DAG逆拓扑求SG。对点u,把相同颜色的所有出边终点SG异或成一个向量,选择颜色集合得到这些向量的任意子集异或,也就是GF(2)张成空间。用201位线性基求该空间mex:若最低缺失的主元位是b,则mex为2^b。无出边点SG为0。最后所有石子所在点SG异或,非零先手胜。

    正确性说明

    每颗石子是独立公平子游戏,走一步删掉原石子、按颜色产生的石子组合SG为对应终点的异或。所有颜色子集精确给出线性空间(空集合合法)。高位消元基的0到b-1主元存在时能表示全部小于2^b的数,缺b主元则不能表示2^b,所以mex正确。SG组合定理给整体胜负。

    复杂度

    令B=201、W为机器字位宽。拓扑和建边O(N+M),每点初始化B个B位基O(NB²/W),颜色map合并O(MlogM+MB/W),线性基消元O(MB²/W),石子异或O(QB/W)。总空间O(M+NB/W+B²/W+MB/W),包含按颜色聚合的临时向量;N≤200。

    易错点

    不能把大SG截断到64位;本批稠密DAG实测出现2^98。相同颜色及重复终点要异或合并,不是按边独立选择;无出边即不能操作,不能因为允许空颜色集合就让终点继续删石子。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        cin >> n >> m;
        vector<vector<pair<int, int>>> g(n);
        vector<int> deg(n);
        while (m--) {
            int a, b, c;
            cin >> a >> b >> c;
            g[--a].push_back({--b, c});
            deg[b]++;
        }
        queue<int> q;
        for (int i = 0; i < n; i++)
            if (!deg[i])
                q.push(i);
        vector<int> ord;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            ord.push_back(u);
            for (auto e : g[u])
                if (!--deg[e.first])
                    q.push(e.first);
        }
        vector<bitset<201>> sg(n);
        for (int j = n - 1; j >= 0; j--) {
            int u = ord[j];
            if (g[u].empty())
                continue;
            map<int, bitset<201>> groups;
            for (auto e : g[u])
                groups[e.second] ^= sg[e.first];
            array<bitset<201>, 201> basis;
            for (auto a : groups) {
                auto v = a.second;
                for (int b = 200; b >= 0; b--)
                    if (v[b]) {
                        if (basis[b].none()) {
                            basis[b] = v;
                            break;
                        }
                        v ^= basis[b];
                    }
            }
            int b = 0;
            while (b <= 200 && basis[b].any())
                b++;
            if (b <= 200)
                sg[u][b] = 1;
        }
        int stones;
        cin >> stones;
        bitset<201> ans;
        while (stones--) {
            int u;
            cin >> u;
            ans ^= sg[--u];
        }
        cout << (ans.any() ? 1 : 0) << '\n';
    }
    
    • 1

    信息

    ID
    1824
    时间
    1000ms
    内存
    512MiB
    难度
    提高
    标签
    递交数
    1
    已通过
    1
    上传者