20181228 模拟赛 T3 字符串游戏 strGame 博弈论 字符串
3 字符串游戏(strGame.c/cpp/pas)
3.1 题目描述
pure 和 dirty 决定玩 T 局游戏。对于每一局游戏,有n个字符串,并且每一局游戏由K轮组成。具体规则如下:在每一轮游戏中,最开始有一个空串,两者轮流向串的末尾添加一个字符,并且需要保证该串为n个字符串中任意一个串的前缀,不能操作的人输掉这一轮,并且在下一轮游戏中由该轮输掉的人先手。另外为了遵循女士优先的原则,在每一局游戏的第一轮均由 pure 先手。
玩家的目标是获得整局游戏的胜利,一局游戏的胜利条件是:对手输掉最后一轮游戏。我们可以假定pure和dirty都足够聪明。
现在,对于每一局游戏,pure 想知道获胜者是谁。
3.2 输入格式
第一行一个整数 T,表示游戏局数。
接下来 T 组数据,每组数据第一行两个整数 n, K,表示字符串数和轮数,接下来n行,每行一个字符串。
3.3 输出格式
对于每一局游戏,输出一行``Pure''或者``Dirty''表示获胜者。
3.4 样例输入
2
2 3
a
b
1 2
ab
3.5 样例输出
Pure
Dirty
3 .6 数据范围与约定
对于 10% 的数据,字符串总长不超过5,且 K <= 2 ;
对于 20% 的数据,字符串总长不超过5;
对于另外 20% 的数据,K = 1;
对于 100% 的数据,1 <= n <= 105; 1 <= K <= 109; 1 <= T <= 10,每局游戏字符串总长不超过 10^5,其中字符串非空且均为小写英文字母。
分析:
原来一直以为博弈论是玄学中的玄学,今天初见,原来也是一门非常美妙严谨的,玄学中的科学呢。
我并不想谈SG函数,也就是说,假如你博弈论基础为0的话呢,也是可以看懂这篇报告的。
仔细审题后我们会发现,我们要处理这些串的问题,又不能随便枚举,所以很容易想到建一棵trie树!(上来就想建各种自动机的dalao我只能跪%了)
插一句话,据说在博弈论题目中呢,有一个套路就是把每次的局面当成节点,于是乎整个游戏的所有局面构成了一棵树(有的时候是DAG),随着游戏局面的推进,相当于在这张有向图上越走越深,这道题中显然只是一棵树,因为我们不可能不断加字母,完了还回到了一个之前状态的局面,那长度也不对劲啊!
我们惊奇的发现,这棵所谓的局面树,和trie树差不多……哎!好像是一回事啊,不管是谁加字母,只会在这个树上越走越深,无字母可加时,这一方就失败了!
所以我们可以了解,当一个点无路可走的时候,那这个点就是必败的状态。
进而,我们可以利用回溯的过程,倒着求出每个点的状态,当一个点仅指向一个必败的点时,这个点就是必胜的,当这个点仅仅指向必胜的点时,这个点就是必败的(因为要交替进行嘛),当一个点既指向必胜又指向必败点时,这个点是既有胜的能力,又有败的能力的,当一个点仅仅指向一个胜败皆可的点时,它是既没有胜的能力也没有败的能力的(胜负完全掌握在对方手上)。
所以,处于一个点时,所具备的对局势主导的能力只由这个点的所有子节点决定。
并且,当前点的能力,就是它所有子节点的能力反过来的并集(即位运算的或)。
所以我们用两位二进制来表示具备的输赢的能力,0表示都不具备,1表示只具备输的能力,2表示只具备赢的能力,3表示掌握大局(既能赢也能输),所以我们就可以想出递推式子:f[a] |= (f[ch[a][i]] ^ 3);(ch[a][i]是一个存在的子节点)
这样呢,我们递推结束之后,只需要判断开始的节点(先手或后手)有怎样的能力,以及能否获得(先手或后手)的机会,来判断是否能取得最终的胜利。
(获得先手机会的手段当然是一直输,最后一把你就是先手了)
代码:
1 #include<bits/stdc++.h> 2 #define ms(a,x) memset(a,x,sizeof(a)) 3 using namespace std; 4 const int N=100005; 5 int n,k,ch[N][26],p,f[N];char s[N]; 6 void ins(){ 7 int t=1,len=strlen(s+1); 8 for(int i=1;i<=len;i++){ 9 int c=s[i]-'a'; 10 if(!ch[t][c]) ch[t][c]=++p; 11 t=ch[t][c]; 12 } return ; 13 } void dfs(int a){ 14 bool flg=0; 15 for(int i=0;i<26;i++){ 16 if(ch[a][i]){ 17 dfs(ch[a][i]),flg=1; 18 f[a]|=(f[ch[a][i]]^3); 19 } 20 } if(!flg) f[a]=1; 21 } int main(){ 22 freopen("strGame.in","r",stdin); 23 freopen("strGame.out","w",stdout); 24 int t;scanf("%d",&t); 25 while(t--){ 26 scanf("%d%d",&n,&k); 27 ms(ch,0);ms(f,0);p=1; 28 for(int i=1;i<=n;i++) 29 scanf("%s",s+1),ins(); 30 dfs(1);if(f[1]==3)puts("Pure"); 31 else if(f[1]==2&&(k&1)) 32 puts("Pure"); 33 else puts("Dirty"); 34 } return 0; 35 }