Codeforces455 B. A Lot of Games(DAG上博弈,字典树上dfs)

题意:

给定n个字符串,一开始有一个空串。
两个人轮流操作,每次操作将一个字符加到空串后面,要求加入新字符之后串必须是某个串的前缀。
最后不能进行操作的人输。

总共进行k次比赛,第k次比赛输的人才是真的输。
第x次比赛输的人在第x+1次比赛作为先手。

问最后谁赢。

数据范围:n<=1e5,k<=1e9

解法:

每个前缀对自己的下一个前缀建有向边,那么这个博弈就是DAG上博弈

但是暴力建边会出现重复边,因为是前缀所以想到字典树,那么问题就变成字典树上的DAG博弈了。

因为:
总共进行k次比赛,第k次比赛输的人才是真的输。
第x次比赛输的人在第x+1次比赛作为先手。

所以对于每个节点,需要定义 必胜,必败,胜负可控,胜负被控 4种状态。
然后在字典树上dfs自底向上计算出先手的状态。

如果第一次比赛先手是可控,那么最后一定赢。
如果第一次比赛先手是被控或者必败(这时候先手一定输,会演变成k次都是先手在比且都输),最后一定输。
否则就是轮流先手,根据k的奇偶性判断胜负即可。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
struct Trie{
    int a[maxm][26],tot;//数组大小为(总长度*字符集大小)
    int d[maxm];
    void add(char *s){
        int len=strlen(s+1);
        int node=0;
        for(int i=1;i<=len;i++){
            int v=s[i]-'a';
            if(!a[node][v])a[node][v]=++tot;
            node=a[node][v];
        }
    }
    void dfs(int x){//0必败,1必胜,2可控,3被控
        int num=0;
        int cnt[4]={0};
        for(int i=0;i<26;i++){
            if(a[x][i]){
                int v=a[x][i];
                dfs(v);
                cnt[d[v]]++;
                num++;
            }
        }
        if(!num){//没路走了,必败
            d[x]=0;return ;
        }
        if(cnt[0]&&cnt[1]){//可控
            d[x]=2;return ;
        }
        if(cnt[3]){//令对方被控,可控
            d[x]=2;return ;
        }
        //!cnt[3]
        if(cnt[0]){
            d[x]=1;return ;
        }
        if(cnt[1]){
            d[x]=0;return ;
        }
        if(cnt[2]){//对方可控,被控
            d[x]=3;return ;
        }
    }
}T;
char s[maxm];
int n,k;
signed main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        T.add(s);
    }
    T.dfs(0);
    if(T.d[0]==2){//可控
        puts("First");
    }else if(T.d[0]==3||T.d[0]==0){//被控/输
        puts("Second");
    }else{
        if(k&1){
            puts("First");
        }else{
            puts("Second");
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107923767