1 条题解

  • 0
    @ 2025-8-24 22:22:54

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar loveJY
    世皆纵横,唯我彳亍

    搬运于2025-08-24 22:22:54,当前版本为作者最后更新于2020-08-20 15:34:49,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    SG定理好题

    首先一个树在经过一次删除操作后,会变成许多森林

    然后这些森林其实就是原问题局面的一些子局面,那么不难记一个局面就是一个树,然后会发现不同的树之间的操作是不会产生影响的,所以我们把所有树的SG值异或起来,就可以计算出在这次操作之后子局面的SG值

    同时由SG定理,原树的SG值就是所有不同删除操作之后得到的子局面的SG值的mex,所以我们就是要快速求出所有这样的SG值的mex

    仔细想想,如果一次删除操作删在根,那么直接把根每个子树的SG值异或起来就能得到剩下的了

    而如果删除操作在一颗子树内,那么把这棵子树外其他子树的SG值异或起来,再把这棵子树的操作完之后得到的SG集合都和那个值异或来转移

    所以我们可以对于每棵子树记录一个SG集合表示子树内删除每个点后分别得到的后继SG值集合

    然后我们需要一个全局异或,单点插入,快速合并以及支持查询全局mex的数据结构来维护

    使用01trie即可,这样我们就能在O(nlogn)O(nlogn)的时间内完成此题

    code:

    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    const int MAXN = 5e5 + 7;
    const int MAXM = 1e6 + 7;
    const int MAXT = 5e6 + 7;
    using namespace std;
    
    int T, n, m, ccnt, ans;
    int home[MAXN], nxt[MAXM], to[MAXM], vis[MAXN], rt[MAXN];
    int sg[MAXN];
    inline void ct(int x, int y) {
    	ccnt++;
    	nxt[ccnt] = home[x];
    	home[x] = ccnt;
    	to[ccnt] = y;
    }
    
    const int up = 20;
    
    namespace trie {
    #define swap(x,y) (x^=y^=x^=y)
    	int ls[MAXT], rs[MAXT], nd, siz[MAXT], tag[MAXT];
    	inline void pushdown(int x, int dep) {
    		if(tag[x]) {
    			if(ls[x])tag[ls[x]] ^= tag[x];
    			if(rs[x])tag[rs[x]] ^= tag[x];
    			if(tag[x] & (1 << dep))swap(ls[x], rs[x]);
    			tag[x] = 0;
    		}
    	}
    	inline void pushup(int x) {
    		siz[x] = siz[ls[x]] + siz[rs[x]];
    	}
    	inline void ins(int &x, int v, int dep = up) {
    		if(!x)x = ++nd,
    				  ls[nd] = rs[nd] = siz[nd] = tag[nd] = 0;
    		if(dep == -1)return (void)(siz[x] = 1); //重复的只算一个
    		pushdown(x, dep);
    		if(v & (1 << dep))ins(rs[x], v, dep - 1);
    		else ins(ls[x], v, dep - 1);
    		pushup(x);
    	}
    	inline int merge(int x, int y, int dep = up) {
    		if(!x || !y)return x | y;
    		pushdown(x, dep);
    		pushdown(y, dep);
    		ls[x] = merge(ls[x], ls[y], dep - 1);
    		rs[x] = merge(rs[x], rs[y], dep - 1);
    		if(dep != -1)pushup(x);
    		return x;
    	}
    	inline int query(int x, int dep = up) {
    		if(!x)return 0;
    		if(siz[ls[x]] < (1 << dep))return query(ls[x], dep - 1);
    		else return query(rs[x], dep - 1) | (1 << dep);
    	}
    };
    using namespace trie;
    
    inline void init() {
    	nd = 0;
    	memset(rt, 0, sizeof(rt));
    	memset(home, 0, sizeof(home));
    	memset(vis, 0, sizeof(vis));
    	ans = 0;
    	ccnt = 0;
    }
    
    inline void dfs(int u, int F) {
    	vis[u] = 1;
    	int S = 0;
    	for(int i = home[u]; i; i = nxt[i]) {
    		int v = to[i];
    		if(v == F)continue;
    		dfs(v, u);
    		S ^= sg[v];
    	}
    	for(int i = home[u]; i; i = nxt[i]) {
    		int v = to[i];
    		if(v == F)continue;
    		tag[rt[v]] ^= (S ^ sg[v]);
    		rt[u] = merge(rt[u], rt[v]);
    	}
    	ins(rt[u], S);
    	sg[u] = query(rt[u]);
    	return ;
    }
    
    int main() {
    	scanf("%d", &T);
    	while(T-- > 0) {
    		init();
    		scanf("%d%d", &n, &m);
    		for(int i = 1, x, y; i <= m; ++i) {
    			scanf("%d%d", &x, &y);
    			ct(x, y);
    			ct(y, x);
    		}
    		for(int i = 1; i <= n; ++i)
    			if(!vis[i]) {
    				dfs(i, 0);
    				ans ^= sg[i];
    			}
    
    		if(ans)
    			printf("Alice\n");
    		else printf("Bob\n");
    	}
    	return 0;
    }
    
    
    • 1

    [清华集训 2016] Alice 和 Bob 又在玩游戏

    信息

    ID
    5597
    时间
    2000ms
    内存
    500MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者