bzoj4730

来源:互联网 发布:志鸿优化设计历史答案 编辑:程序博客网 时间:2024/06/11 01:54

Alice 和 Bob 又在玩游戏。

有 nn 个节点,mm 条边(0mn10≤m≤n−1),构成若干棵有根树,每棵树的根节点是该连通块内编号最小的点。

Alice 和 Bob 轮流操作(Alice 先手),每回合选择一个没有被删除的节点 xx,将 xx 及其所有祖先全部删除,不能操作的人输。

需要注意的是,树的形态是在一开始就确定好的,删除节点不会影响剩余节点父亲和儿子的关系。

比如:1-3-2 这样一条链,1 号点是根节点,删除 1 号点之后,3 号点还是 2 号点的父节点。

假设 Alice 和 Bob 都足够聪明,问 Alice 有没有必胜策略。

输入格式

第一行一个正整数 TT,表示该测试点有 TT 组数据;接下来 TT 组数据。

对于每组数据:

输入第一行两个整数 nnmm,分别表示点数和边数(节点从 11 开始编号)。

接下来 mm 行,每行两个正整数 aabb,表示节点 aa 和节点 bb 之间有一条边,输入数据中没有重边。

输出格式

输出 TT 行,每行输出 Alice 先手并且 Alice 和 Bob 都足够聪明的情况下谁获胜。

样例

input

42 11 23 21 21 32 03 11 2

output

AliceAliceBobAlice

explanation

输入共 4 组数据;

第一组数据输入是一条链,Alice 可以一次性把所有节点都删掉。

第二组数据,Alice 先手第一步删除 1 号点即可胜利。

限制与约定

对于10%10%的数据,m=0m=0;

对于20%20%的数据,1n201≤n≤20;

对于40%40%的数据,1n1021≤n≤102

对于60%60%的数据,1n1031≤n≤103

对于100%100%的数据,1T10,1n105,n2×105,0mn11≤T≤10,1≤n≤105,∑n≤2×105,0≤m≤n−1,输入数据保证不会形成环,且每棵树的大小5×104≤5×104

时间限制2s

对于一个子树的根为root,假定x在以root为根的子树中,那么删除x到根的所有节点之后剩下的游戏状态就是son[z](其中z在x到root的路径上),也就是删掉x的后继状态就是son[z]的sg1值的异或和,那么root的sg值就是他的所以子节点的sg1值的mex。(注意区分sg1和sg)

然后我们发现对于每个根节点他的儿子的sg1值都是不断在变化的,对于一个x,他的sg1值为sg1[son[x]],以及sg1[son[root]](其中x不在son[root]为根的子树中),我们用一个trie维护,每个trie维护的就是某个点到以一个特定点为root的sg1值,假如我们对于一个z已经维护了它到x的sg1值,那么它到fa[x]的sg1值就是相当于它的所有sg1值异或上sg1[son[fa[x]]其中son[fa[x]]!=x。如果用trie维护一个点到某个点的sg1值,那么我们就只要异或上一个数就行了,当然还有支持sg1的合并也就是trie的合并。实现中有一些细节需要注意。

#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int MAXN = 100005;int f[MAXN], i, n, j, m, k, l, x, y, T;int first[MAXN], next[MAXN << 1], go[MAXN << 1], t, size[MAXN * 21];int root[MAXN], s[MAXN * 21][2], nn, d[20], len, cnt[MAXN * 21], ans;bool vis[MAXN];inline void add(const int &x, const int &y){next[++t] = first[x]; first[x] = t; go[t] = y;}inline int get(){char c;while ((c = getchar()) < 48 || c > 57);int res = c - 48;while ((c = getchar()) >= 48 && c <= 57)res = res * 10 + c - 48;return res;}inline void rev(int x, int dep, int y){cnt[x] ^= y;if ((y >> (nn - dep - 1)) & 1)swap(s[x][0], s[x][1]);}inline void putdown(int x, int dep){if (cnt[x]){if (s[x][0]) rev(s[x][0], dep + 1, cnt[x]);if (s[x][1]) rev(s[x][1], dep + 1, cnt[x]);cnt[x] = 0;}}inline void insert(int x, int sum){for(int i = 1; i < nn; i ++)d[i] = ((sum >> (nn - i - 1)) & 1);size[x] ++;for(int i = 1; i < nn; i ++){if (!s[x][d[i]]) s[x][d[i]] = ++len;x = s[x][d[i]];size[x] ++;}}inline int merge(int x, int y, int dep){if (!x) return y;if (!y) return x;putdown(y, dep);s[x][0] = merge(s[x][0], s[y][0], dep + 1);s[x][1] = merge(s[x][1], s[y][1], dep + 1);size[x] = size[s[x][0]] + size[s[x][1]];if (dep == nn) size[x] = 1;return x;}inline void dfs(int now, int fa){vis[now] = 1; f[now] = 0;int tot = 0;for(int i = first[now]; i; i = next[i])if (go[i] != fa) dfs(go[i], now), tot ^= f[go[i]];insert(root[now] = ++len, tot);for(int i = first[now]; i; i = next[i])if (go[i] != fa){rev(root[go[i]], 1, tot ^ f[go[i]]);root[now] = merge(root[now], root[go[i]], 1);}int x = root[now];for(int i = 1; i < nn; i ++){if (!x) break;putdown(x, i);if (size[s[x][0]] == (1 << (nn - i - 1))) f[now] += (1 << (nn - i - 1)), x = s[x][1];else x = s[x][0];}if (x) f[now] ++;}int main(){cin >> T;while (T --){len = t = 0;cin >> n >> m;nn = log2(n) + 2;memset(vis, 0, sizeof(vis));memset(first, 0, sizeof(first));memset(s, 0, sizeof(s));memset(cnt, 0, sizeof(cnt));memset(size, 0, sizeof(size));for(i = 1; i <= m; i ++){x = get(); y = get();add(x, y);add(y, x);}ans = 0;for(i = 1; i <= n; i ++)if (!vis[i]) dfs(i, 0), ans ^= f[i];if (ans) cout << "Alice" << endl;else cout << "Bob" << endl;}}


0 0
原创粉丝点击