peiwenjun's blog 没有知识的荒原

CF1037G A Game on Strings 题解

题目描述

Alice 和 Bob 正在玩游戏,给定长为 \(n\) 的字符串 \(t\) ,接下来两人轮流操作:

  • 选择一个字符串,从中任选一个字母 \(c\) ,删去所有 \(c\) 出现的位置,每一段成为一个新的字符串。

Alice 先手,不能操作的人输。

\(m\) 次询问,给定 \(l,r\) ,求初始字符串为 \(t_{l,r}\) 时,谁有必胜策略。

数据范围

  • \(1\le n,m\le 10^5,1\le l\le r\le n\)

时间限制 \(\texttt{4s}\) ,空间限制 \(\texttt{256MB}\)

分析

有一个显然的 \(\mathcal O(n^2+m)\) 暴力。令 \(f_{l,r}\) 表示子串 \(t_{l,r}\)\(\text{sg}\) 值,转移方程如下:

\[f_{l,r}=\text{mex}_{c\in t_{l,r}}\big(f_{l,p_1-1}\oplus\cdots\oplus f_{p_k+1,r}\big) \]


\(t_{l-1}=c\)\(t_{r+1}=c\) ,并且 \(c\not\in t_{l,r}\) 的区间为好区间。

容易发现好区间仅有 \(\mathcal O(26n)\) 个,并且仅有好区间会被递归计算。

预处理所有好区间的答案,转移用前缀和优化到 \(\mathcal O(1)\) ,询问枚举第一步选择的字符即可。

再讲一下实现细节。

pre[i][c],nxt[i][c]\(i\) 前后第一次出现字符 \(c\) 的位置。

首先考虑如何存储好区间,记 fl[i][c],fr[i][c] 为区间 \([pre_{i,c}+1,i]\)\([i,nxt_{i,c}-1]\) 的答案。

从左往右扫描 \(r\) ,考虑预处理所有右端点为 \(r\) 的区间的答案。

\(val_r\) 为前缀 \(t_{1,r}\) 删掉字符 \(t_r\) 后所有区间 \(\text{sg}\) 值的异或和,用于前缀和优化。

\(c=0,\cdots,25\) 按照 pre[r][c] 降序排序,依次计算这些 \(fl_{r,c}\) 的答案。

对于 \(fr\) 的处理,合法的左端点 \(\in[pre_{r+1,s_{r+1}}+1,r]\)

容易发现转移刚好只会用到已经计算过的状态。

时间复杂度 \(\mathcal O(26^2n+26m)\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int l,m,n,r;
char s[maxn];
int pre[maxn][26],nxt[maxn][26];
int fl[maxn][26],fr[maxn][26],val[maxn];
vector<int> vec(26);
int calc(int l,int r)
{
    if(l>r) return 0;
    bitset<32> b;
    for(int c=0;c<26;c++)
        if(nxt[l][c]<=r)
            b[fr[l][c]^val[nxt[l][c]]^val[pre[r][c]]^fl[r][c]]=1;
    for(int i=0;;i++) if(!b[i]) return i;
}
int main()
{
    scanf("%s%d",s+1,&m),n=strlen(s+1);
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<26;j++) pre[i][j]=pre[i-1][j];
        pre[i][s[i]-'a']=i;
    }
    for(int j=0;j<26;j++) nxt[n+1][j]=n+1,vec[j]=j;
    for(int i=n;i>=1;i--)
    {
        for(int j=0;j<26;j++) nxt[i][j]=nxt[i+1][j];
        nxt[i][s[i]-'a']=i;
    }
    for(int r=1;r<=n;r++)
    {
        int lst=pre[r-1][s[r]-'a'];
        val[r]=val[lst]^calc(lst+1,r-1);
        sort(vec.begin(),vec.end(),[&](int x,int y){return pre[r][x]>pre[r][y];});
        for(auto c:vec) fl[r][c]=calc(pre[r][c]+1,r);
        if(r!=n) for(int c=s[r+1]-'a',j=r;j>pre[r][c];j--) fr[j][c]=calc(j,r);
    }
    for(int i=1;i<=m;i++) scanf("%d%d",&l,&r),printf(calc(l,r)?"Alice\n":"Bob\n");
    return 0;
}

posted on 2023-05-31 20:42  peiwenjun  阅读(15)  评论(0)    收藏  举报

导航