首页 > 代码库 > 【East!模拟赛】【Round1】【codeforces455B】题解。
【East!模拟赛】【Round1】【codeforces455B】题解。
A:codeforces Round#260 div1 B [a lot of games].
题解:俩人玩游戏,有若干个字符串,每一轮都是俩人轮流念一个字母,使得当前的这些字母是其中一个字符串或者其前缀,即在字典树上走,每人走一步,走不了的人输,然后有m轮,每轮输的下一轮先手,问最后一轮谁赢?
题解:
显然这是一道博弈题,但是如果我们单纯地计算每一局是先手赢还是后手赢,那就要跪了。
因为:先手可以选择输,以保证下一局的先手,然后一直先手,最后一局再选择赢!!!
那么我们就需要多判断几种状态。
首先有一种朴素的算法(不仅朴素还难写),是树上每个点枚举9种状态,即:
1.可以赢,可以输。
2.可以赢,输不输看对手。
3.可以胜,一定输。
4.赢不赢看对手,可以输。
……
一共九种,赢的一面有三种:必胜,看对手,必败,输的同理,乘一下9种。
当然,其中我们可以剪掉一些,但是!!依然很难写!!!
于是就有了进一步思考后的分析。
我们可以分成四种状态,即可胜可负,必胜,必败,胜负都看对手。
然后分别对应3,2,1,0
这样可以ans|=dfs(v)^3;
满足条件。
为什么可以这样呢?这真的对么?这真的对。要不codeforces那么强力的hack也过不了~~
正确性:3、2、1应该都不用说了。要说的应该只有0状态。
0状态:
首先游戏是正向进行的,,虽然DP是从叶子开始推的。。
然后某人进行选择,那么他的对手就可以有针对性地进行选择,不让这个“某人”如愿以偿,要么是怒赢,要么是故意输然后拿先手。
正确性证明完毕。
然后总体思路就是建Trie树,然后得出先手是0、1、2、3哪种状态,然后就可以O(1)出解了~~
贴代码:(要在codeforces上交需要删freopen)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 101000 #define T 130 using namespace std; char s[N]; int n,m; struct Trie { int next[220000][T],cnt; void insert() { int i,x,alp; scanf("%s",s); for(i=x=0;s[i];i++) { alp=s[i]; if(!next[x][alp])next[x][alp]=++cnt; x=next[x][alp]; } return ; } int dfs(int x) { int i,v,ans=0; bool flag=1; for(i=0;i<T;i++)if(v=next[x][i]) { flag=0; ans|=dfs(v)^3; } if(flag)return 1; return ans; } }trie; int main() { freopen("A.in","r",stdin); freopen("A.out","w",stdout); int i,ans; scanf("%d%d",&n,&m); if(m==0)puts("Sword!"); else { for(i=1;i<=n;i++)trie.insert(); ans=trie.dfs(0); if(ans==3||(ans==2&&m&1))puts("Sword!"); else puts("Oh!no!"); } fclose(stdin); fclose(stdout); return 0; }
【East!模拟赛】【Round1】【codeforces455B】题解。