XJOI模拟训练11 - C.Game

C.Game

题目描述 :

给出一个由小写英文字母构成的字符串 S,再取 n 个字符串,它们都是 S 的子串,之后开始游戏,两人轮流操作: 每次选一个串,在其后添加一个英文字母,保证添加后该串仍为 S 的子串。 谁不能操作谁输,或者谁不能操作谁胜。 但是春希看出了胜负已定,于是对实际操作没有兴趣,所以 Pickupwin 只要求他指明先手是否必胜就好。

输入格式 :

第一行一个字符串 S;
第二行一个正整数 n;
接下来 n 行,每行两个正整数 l,r,表示第 i 个字符串由 S 中第 l 个至第 r 个字符一次拼接而成(S 中字符从 1 开始标号)

输出格式 :

输出 n + n行
\(2 * i - 1\) 行输出在仅有前 i 个子串时,谁不能操作谁输,先手是否必胜。
\(2 * i\) 行输出在仅有前 i 个子串时,谁不能操作谁胜,先手是否必胜。
用 Fir 表示先手必胜, Sec 表示后手必胜。

样例 :

输入 :

sroababorz
3 
4 5 
7 8 
1 3

输出 :

Fir 
Sec 
Fir 
Sec 
Sec 
Fir

数据范围 :

50% |S| <= 200,n <= 100
100% |S| <=\(10^6\), n <= \(5*10^5\)

Solution :

Anti-Nim游戏 :

定义:
桌子上有 N 堆石子,游戏者轮流取石子。
每次只能从一堆中取出任意数目的石子,但不能不取。
取走最后一个石子者败。
结论:
先手必胜当且仅当:
2. 所有堆的石子数都为 1 且游戏的 SG 值为 0; 
3. 有些堆(>=1)的石子数大于 1 且游戏的 SG 值不为 0。

所以我们可以先对字符串 S 建一个后缀自动机,在SAM上跑出每个位置对应的 SG 函数值,对于给出的每个子串,用倍增找到它在 SAM 上对应的位置,得到相应的 SG值,再依据 SG 函数值将它抽象为 Nim游戏,利用Nim和Anti-Nim的结论来判断即可。

Code :

#include<bits/stdc++.h>
using namespace std;
const int N = 2e6 + 7, L = 1e6 + 7;
char s[L];
int n, m, l, r, len;
int id[L], SG[N];
template<class T> void read(T &x) {
    bool f = false; x = 0;
    char ch = getchar();
    while (ch<'0' || ch>'9') {if (ch == '-') f = true; ch = getchar();}
    while (ch>='0' && ch<='9') x = x * 10 + ch - '0', ch = getchar();
    if (f) x = -x;
}
namespace sam {
    int size = 1, prv = 1, cnt = 0;
    int maxlen[N], trans[N][27], link[N], buf[N], a[N];
    int fa[N][25], fir[N], nxt[N], to[N], d[N];
    inline void Extend(int id, int pos) {
        int cur = ++size, u;
        d[pos] = cur;
        // cout<<d[pos]<<" : pos"<<endl;
        maxlen[cur] = maxlen[prv] + 1;
        for (u = prv; u && !trans[u][id]; u = link[u]) trans[u][id] = cur;
        if (u == 0) link[cur] = 1;
        else {
            int v = trans[u][id];
            if (maxlen[v] == maxlen[u] + 1) link[cur] = v;
            else {
                int clo = ++size;
                maxlen[clo] = maxlen[u] + 1;
                memcpy(trans[clo], trans[v], sizeof(trans[v]));
                link[clo] = link[v];
                for (; u && trans[u][id] == v; u = link[u]) trans[u][id] = clo;
                link[cur] = link[v] = clo;
            }
        }
        prv = cur;
    }
    inline void AddEdge(int u, int v) {
        nxt[++cnt] = fir[u];
        fir[u] = cnt, to[cnt] = v;
    }
    inline void dfs(int u) {
        fa[u][0] = link[u];
        for (int i = 1; i <= 21; i++) {
            fa[u][i] = fa[fa[u][i-1]][i-1];
        }
        for (int i = fir[u]; i; i = nxt[i]) {
            int v = to[i];
            dfs(v);
        }
    }
    inline int getmex(vector<int> &V) {
        sort(V.begin(), V.end());
        unique(V.begin(), V.end());
        int res = 0;
        for (int i = 0; i < (int)V.size(); i++) {
            if (V[i] == res) res ++;
            else return res;
        }
        return res;
    }
    inline void getSG() {
        for (int i = 1; i <= size; i++) buf[maxlen[i]]++;
        for (int i = 1; i <= size; i++) buf[i] += buf[i - 1];
        for (int i = 1; i <= size; i++) a[buf[maxlen[i]]--] = i;
        for (int i = size; i >= 1; i--) {
            vector<int> A;
            for (int j = 0; j < 26; j++) {
                if (trans[a[i]][j]) {
                    A.push_back(SG[trans[a[i]][j]]);
                }
            }
            if (A.empty()) SG[a[i]] = 0; else SG[a[i]] = getmex(A);
        }
    }
    inline int getpos(int l, int r) {
        int x = d[r];
        // cout<<x<<"  : x "<<maxlen[x]<<" :len"<<endl;
        for (int i = 21; i >= 0; i--) {
            if (maxlen[fa[x][i]] >= r - l + 1) x = fa[x][i];
        }
        return x;
    }
}
using namespace sam;
int main() {
    scanf("%s", s + 1);
    len = strlen(s + 1);
    for (int i = 1; i <= len; i++) {
        Extend(s[i] - 'a', i);
    }
    for (int i = 2; i <= size; i++) AddEdge(link[i], i);
    dfs(1); getSG();
    read(n);
    for (int i = 1; i <= n; i++) {
        read(l), read(r);
        id[i] = getpos(l, r);
    }
    int nw = 0, flag = true;
    for (int i = 1; i <= n; i++) {
        nw ^= SG[id[i]];
        if (nw) puts("Fir");
        else puts("Sec");
  
        if (SG[id[i]] > 1) flag = false;
        if ((flag && !nw) || (!flag && nw)) puts("Fir");
        else puts("Sec");
    }
    return 0;
}
posted @ 2019-10-11 22:18  Refaint  阅读(205)  评论(0)    收藏  举报