NKOJ P8949 字符游戏

问题描述

对于两个字符串,如果它俩互不为对方的前缀,我们称它们互为“异串”。
比如:"\(010\)" 和"\(1111\)"就互为“异串”。“\(0001\)”和"\(00\)"就不是,因为"\(00\)"是"\(0001\)"的前缀。

Alice在一张纸上写下了\(N\)个字符串,每个字符串都是由\(0,1\)构成。任意两个字符串都互为“异串”,且字符串的长度都不超过\(L\)

Alice和Bob开始玩“字符游戏”,他俩轮流往纸上添加一个新字符串。
要求,添加的字符串由\(0,1\)构成,长度在\([1,L]\)之间,且与当前纸上的串都互为“异串”。
谁无法添加,谁就输掉游戏。
Alice总是先手,问谁有必胜策略。

\(1 \leq N \leq 10^5\)

\(N\)个字符串长度保证不超过\(10^5\)

\(1 \leq L \leq 10^{18}\)

Solution

看到\(01\)字符串和处理判断前缀,想到\(Trie\)树,将\(Trie\)树建出来,发现在叶子节点为根的子树内节点都无法再次到达(否则就出现了前缀),又发现,我们建完\(Trie\)树后,若某节点不是叶子,且还有未去过的儿子,那么会将这个游戏分成若干个相同规则的游戏。

这是一个\(multi-SG\)问题。

所以我们只需要算出单一游戏的\(SG\)值,最后将其异或起来,即可得到局面\(SG\)

考虑如何计算单一游戏\(SG\)值,定义高度为从下往上数的层数。

在草稿纸上多画画,会发现如下规律:
\(SG(1) = 1, SG(2) = 2,SG(3) = 1,SG(4) = 4,SG(5) = 1,SG(6) = 2...\)

那么\(SG(x)=lowbit(x)\)

于是这道题就做完了。

\(code:\)

#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 100000 + 5;
typedef long long ll;
struct Trie{
	int nxt[2];
	int num;
}trie[MAX_N<<5];
int n,tot=1;
ll L;
void insert(string x){
	int p=1;
	for(int i=0;i<x.size();i++){
		int c=x[i]-'0';
		if(!trie[p].nxt[c]){
			trie[p].nxt[c]=++tot;
			trie[p].num=0;
			p=trie[p].nxt[c];
		}
		else{
			p=trie[p].nxt[c];
		}
	}
	trie[p].num++;
}
ll ans=0;
ll sg(ll x){return x&-x;}
void solve(int x,ll dep){  
	int cnt=0;
	if(trie[x].nxt[0]) cnt++,solve(trie[x].nxt[0],dep+1);
	if(trie[x].nxt[1]) cnt++,solve(trie[x].nxt[1],dep+1);
	if(cnt==1){
		ans^=sg(L-dep);
	} 
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>L;
	int maxn=0;
	for(int i=1;i<=n;++i){
		string s;
		cin>>s;
		insert(s);
	}
	solve(1,0);
	if(ans) printf("Alice");
	else printf("Bob");
	return 0;
}
posted @ 2021-11-29 16:15  Thermalrays  阅读(63)  评论(1编辑  收藏  举报