[平等博弈][SG函数][字典树合并] BZOJ 4730: Alice和Bob又在玩游戏

5 篇文章 0 订阅
4 篇文章 0 订阅

Solution S o l u t i o n

Su S u u u 到根的链的子树的SG函数值的集合。
考虑从子树转移到根。
一种是去掉根,就是把所有子树异或起来。
一种操作在子树内,相当于把子树的S异或上子树的SG异或和。
集合异或上一个数可以打标记。
集合的合并可以用字典树合并。
mex mex 可以二分。

#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;
typedef long long ll;
typedef pair<int, int> Pairs;

const int N = 202020;
const int M = 4040404;
const int K = 18;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}

namespace trie {
    int tcnt;
    int ch[M][2];
    int rt[N], tag[M], sz[M];
    inline void mark(int u, int x, int d) {
        if (d < 0 || !u) return;
        tag[u] ^= x;
        if (x >> d & 1) swap(ch[u][0], ch[u][1]);
    }
    inline void pushUp(int u) {
        sz[u] = sz[ch[u][0]] + sz[ch[u][1]];
    }
    inline void pushDown(int u, int d) {
        if (tag[u]) {
            mark(ch[u][0], tag[u], d - 1);
            mark(ch[u][1], tag[u], d - 1);
            tag[u] = 0;
        }
    }
    inline void _insert(int &u, int x, int d) {
        if (!u) u = ++tcnt;
        if (d < 0) { sz[u] = 1; return; }
        pushDown(u, d);
        _insert(ch[u][x >> d & 1], x, d - 1);
        pushUp(u);
    }
    inline int _merge(int x, int y, int d) {
        if (!x || !y) return x ? x : y;
        if (d < 0) {
            sz[x] |= sz[y]; return x;
        }
        pushDown(x, d); pushDown(y, d);
        ch[x][0] = _merge(ch[x][0], ch[y][0], d - 1);
        ch[x][1] = _merge(ch[x][1], ch[y][1], d - 1);
        pushUp(x); return x;
    }
    inline void mark(int id, int x) {
        mark(rt[id], x, K);
    }
    inline void insert(int id, int x) {
        _insert(rt[id], x, K);
    }
    inline void merge(int from, int to) {
        rt[to] = _merge(rt[from], rt[to], K);
    }
    inline int mex(int id) {
        int res = 0, u = rt[id];
        for (int d = K; ~d; d--) {
            pushDown(u, d);
            int dir = (sz[ch[u][0]] == (1 << d));
            res |= dir << d;
            u = ch[u][dir];
        }
        return res;
    }
    inline void reset(int n) {
        for (int i = 1; i <= tcnt; i++) {
            ch[i][0] = ch[i][1] = 0;
            tag[i] = sz[i] = 0;
        }
        for (int i = 1; i <= n; i++) rt[i] = 0;
        tcnt = 0;
    }
}

int test, n, m, ans, gcnt, x, y;
struct edge {
    int to, next;
    edge(int t = 0, int n = 0): to(t), next(n) {}
};
edge G[N << 1];
int head[N], sg[N], vis[N];

inline void addEdge(int from, int to) {
    G[++gcnt] = edge(to, head[from]); head[from] = gcnt;
    G[++gcnt] = edge(from, head[to]); head[to] = gcnt;
}
inline void dfs(int u, int f) {
    int to, sum = 0; vis[u] = 1;
    for (int i = head[u]; i; i = G[i].next) {
        to = G[i].to; if (to == f) continue;
        dfs(to, u); sum ^= sg[to];
    }
    for (int i = head[u]; i; i = G[i].next) {
        to = G[i].to; if (to == f) continue;
        trie::mark(to, sum);
        trie::merge(to, u);
    }
    trie::insert(u, sum);
    sg[u] = trie::mex(u);
    trie::mark(u, sg[u]);
}

int main(void) {
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    read(test);
    while (test--) {
        read(n); read(m);
        ans = gcnt = 0;
        for (int i = 1; i <= n; i++)
            sg[i] = head[i] = vis[i] = 0;
        for (int i = 1; i <= m; i++) {
            read(x); read(y);
            addEdge(x, y);
        }
        for (int i = 1; i <= n; i++) {
            if (vis[i]) continue;
            dfs(i, 0); ans ^= sg[i];
        }
        if (ans) printf("Alice\n");
        else printf("Bob\n");
        trie::reset(n);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值