您的位置:首页 > 其它

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

2018-02-24 19:23 357 查看

SolutionSolution

记SuSu为uu到根的链的子树的SG函数值的集合。

考虑从子树转移到根。

一种是去掉根,就是把所有子树异或起来。

一种操作在子树内,相当于把子树的SS异或上子树的SG异或和。

集合异或上一个数可以打标记。

集合的合并可以用字典树合并。

求mexmex可以二分。

#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
, 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
, sg
, vis
;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: