题目大意:
解题思路:
- 首先我们对于子树u的
S
G
函
数
为
SG函数为
SG函数为
⨁ 是 异 或 和 \bigoplus是异或和 ⨁是异或和
S G [ u ] = m e x { ⨁ w ∈ ( w 的 父 亲 在 u 到 v 的 路 径 上 ) S G [ w ] ∣ v ∈ ( u 的 子 树 里 面 的 点 ) } SG[u]=mex\{\bigoplus_{w\in(w的父亲在u到v的路径上)}SG[w]|v\in(u的子树里面的点)\} SG[u]=mex{w∈(w的父亲在u到v的路径上)⨁SG[w]∣v∈(u的子树里面的点)}
通俗一点就是就是枚举
u
u
u所有的里面的子树节点
v
v
v,然后把分裂出来的子树
w
w
w的
S
G
SG
SG函数
⊕
\oplus
⊕起来就好了
但是这样的复杂度是
O
(
n
3
)
O(n^3)
O(n3)的,我们想办法优化一下?
- 我们看这个 S G 函 数 SG函数 SG函数是自底向上跟新的,那么我们从底向上考虑:
- 我们定义
a u = ⨁ v 是 u 的 直 接 儿 子 S G [ v ] a_u=\bigoplus_{v是u的直接儿子}SG[v] au=v是u的直接儿子⨁SG[v]
b u = ⨁ v 是 u 直 接 儿 子 并 且 包 括 u S G [ v ] b_u=\bigoplus_{v是u直接儿子并且包括u}SG[v] bu=v是u直接儿子并且包括u⨁SG[v]
只考虑相邻两层
那么上面的公式就可以变成:根据异或的消去律 对于一个 v v v
a
u
⊕
(
⨁
w
是
u
和
v
的
路
径
上
面
的
点
&
&
w
不
等
于
v
b
w
)
a_u\;\oplus\;(\bigoplus_{w是u和v的路径上面的点\&\&w不等于v}b_w)
au⊕(w是u和v的路径上面的点&&w不等于v⨁bw)
手模一下你们发现就只剩下点
1
,
2
,
3
,
4
了
1,2,3,4了
1,2,3,4了就刚好是
那么式子就成了。
⨁ w ∈ ( w 的 父 亲 在 u 到 v 的 路 径 上 ) S G [ w ] = ( ⨁ w 是 u 和 v 的 路 径 上 面 的 点 & & w 不 等 于 v b w ) ⊕ a u \bigoplus_{w\in(w的父亲在u到v的路径上)}SG[w]=(\bigoplus_{w是u和v的路径上面的点\&\&w不等于v}b_w)\oplus a_u w∈(w的父亲在u到v的路径上)⨁SG[w]=(w是u和v的路径上面的点&&w不等于v⨁bw)⊕au
-
假设有一棵二进制意义下的 Trie 树,其中储存了所有 u u u 的子树内的 v v v 对应的 ⨁ w 是 u 和 v 的 路 径 上 面 的 点 b w \bigoplus_{w是u和v的路径上面的点}b_w ⨁w是u和v的路径上面的点bw.
-
易得我们需要求得一个最小的数 r e s res res ,使得 a u a_u au异或上 Trie 树储存的任意一个数都不能得到 r e s res res.
-
考虑在 Trie 树上从上往下贪心(从高到低确定 r e s res res的每一位):设当前要确定第 i i i 位(这里二进制位由低往高,最低位为第 0 0 0 位),现在走到 Trie 树的节点 x x x当 a u a_u au的第 i i i 位为 1 1 1 的时候,我们考虑 r e s r e s res 的第 i i i 位可以为 0 0 0 的条件:
-
x x x 的右子节点(由字符为 1 1 1 的边转移到的子节点)对应的子树不是满二叉树,即子树内的叶子数小于 2 i 2^i 2i可以在 Trie 的每个节点上储存(一个 s i z e size size 表示子树内的叶子个数)这时候我们就可以把 r e s r e s res 的第 i i i 位设为 0 0 0 ,否则设为 1 1 1
-
a u a_u au的第 i i i 位为 0 0 0 同理,当 x x x 到达了叶子节点之后就得到了 S G [ u ] = r e s S G [ u ] = r e s SG[u]=res
-
但我们显然不能直接把这棵 Trie 建出来,否则这个 DP 不能得到复杂度上的优化
-
我们考虑一下父子关系:就是自顶向上合并的时候我们发现 ⨁ w 是 u 和 v 的 路 径 上 面 的 点 & & w 不 等 于 v b w \bigoplus_{w是u和v的路径上面的点\&\&w不等于v}b_w ⨁w是u和v的路径上面的点&&w不等于vbw里面 w 不 等 于 v w不等于v w不等于v那么我们向上时候可以把 ( b w [ w = v ] = S G [ v ] ⊕ a u ) 异 或 上 去 (b_w[w=v]=SG[v]\oplus a_u)异或上去 (bw[w=v]=SG[v]⊕au)异或上去然后再把所有的 f a [ v ] fa[v] fa[v]的所有儿子节点的字典树都合并起来就可以了。
-
这个你手模一下父子关系就可以发现了
-
这里是全局的异或 S G [ v ] ⊕ a u SG[v]\oplus a_u SG[v]⊕au,我们要在字典树上面打标记类似线段树吧很好理解,每次递归时候下传就可以了
下放标记的具体方法:如果要下放 Trie 树节点 x x x上的标记 t a g t a g tag ,且连接 x x x 与 x x x 的子节点的转移边表示第 i i i 位,则把 x x x 的左子节点的标记和右子节点的标记都异或上 t a g t a g tag如果 t a g t a g tag 的第 i i i 位为 1 1 1 则需要交换 x x x 的左右子节点(因为这时异或上 t a g t a g tag相当于第 i i i 位被取反),最后将 x x x 节点上的标记清空复杂度 O ( T n l o g n ) O ( T n log n ) O(Tnlogn) -
这么为了不能全局动态开点,我们可以先在里面插入一个 0 0 0
AC code
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 7e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
read(first);
read(args...);
}
int n, m, tot;
int fa[N];
vector<int> G[N];
struct Trie {
int lc, rc, tag, sze;
void init() {
lc = rc = tag = sze = 0;
}
}T[N];
int SG[N], rt[N];
inline int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
inline void init() {
for(int i = 1; i <= n; ++ i) fa[i] = i, G[i].clear();
tot = 0;
}
void downdate(int x, int i) {
if((T[x].tag>>i)&1) swap(T[x].lc,T[x].rc);
if(T[x].lc) T[T[x].lc].tag ^= T[x].tag;
if(T[x].rc) T[T[x].rc].tag ^= T[x].tag;
}
int merge(int i, int x, int y) {
if(!x || !y) return x + y;
if(i == -1) return x;
downdate(x,i), downdate(y,i);
T[x].lc = merge(i-1,T[x].lc,T[y].lc);
T[x].rc = merge(i-1,T[x].rc,T[y].rc);
T[x].sze = T[T[x].lc].sze + T[T[x].rc].sze;
return x;
}
int min_mex_xor(int x, int num) {
int res = 0;
for(int i = 30; i >= 0; -- i) {
downdate(x,i);
if(num >> i & 1) {
if(T[T[x].rc].sze < (1 << i)) x = T[x].rc;
else x = T[x].lc, res |= 1 << i;
} else {
if(T[T[x].lc].sze < (1 << i)) x = T[x].lc;
else x = T[x].rc, res |= 1 << i;
}
}
return res;
}
void ins(int i, int &x, int num ) {
if(!x) T[x = ++ tot].init();
if(i == -1) return (void) (T[x].sze = 1);
downdate(x,i);
if(num >> i & 1) ins(i-1,T[x].rc,num);
else ins(i-1,T[x].lc,num);
T[x].sze = T[T[x].lc].sze + T[T[x].rc].sze;
}
inline int dfs(int u, int fa) {
int au = 0;
rt[u] = 0;
ins(30,rt[u],0);
for(auto it : G[u]) {
if(it == fa) continue;
dfs(it,u);
merge(30,rt[u],rt[it]);
au ^= SG[it];
}
SG[u] = min_mex_xor(rt[u],au);
T[rt[u]].tag ^= (au ^ SG[u]);
return SG[u];
}
int main() {
IOS;
int _;
cin >> _;
while(_--) {
cin >> n >> m;
init();
for(int i = 1; i <= m; ++ i) {
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
int fu = find(u), fv = find(v);
if(fu != fv) {
if(fu > fv) swap(fu,fv);
fa[fv] = fu;
}
}
int ans = 0;
for(int i = 1; i <= n; ++ i)
if(find(i) == i)
ans ^= dfs(i,0);
if(ans) cout << "Alice\n";
else cout << "Bob\n";
}
}