[集训队作业2018] 串串划分

链接

毒瘤题。

首先这么多条件一看就很不好 dp,考虑合并条件 1,2,即我们钦定一个循环串是按照最小循环节分割开的。可以发现这样不符合条件1的串变成了不符合条件2的串。

然后考虑容斥,即“没有相同”看作 \(\sum_{k}(-1)^k\text{至少有k个相同的方案数}\)

考虑一次转移恰好是原串的一次分割,无论这次分割的是否为循环串则有且仅有一种分割。用 \(R(s)\) 表示串 \(s\) 的最小循环节循环次数,即最小正周期,可以列出 \(f_i=\sum_{j}(-1)^{R(s[j+1:i])-1}\ f_j\)

这个式子显然是 \(O(n^2)\) 的(R函数预处理)。

我们发现对于 \(R\) 是奇数的串这么算有点浪费,考虑换一种写法:

\[f_i=\sum_{j=1}^{i-1} f_j-2\times\sum_{j=1}^{i-1}[R(s[i:j])\text{ is even}]f_j \]

但是这样仍然是 \(O(n^2)\) 的。

为了解决这个问题,我们引入一个定义:本原平方串(primitive square)。

定义一个串 \(s\) 是本原平方串,当且仅当其最小正周期恰好为 \(\frac s 2\)

可以证明,任何字符串的本原平方串的个数是 \(O(n\log n)\) 的。

考虑利用这个性质探究循环串 \(s[i:j]\) 的性质:如果 \(R(s[i:j])\text{ is even}\) 必然对应 \(s[i:j]\) 是由若干相同的本原平方串构成。

而一个本原平方串 \(s[i:j]\) 必定对应一个 Runs \((l,r,\frac{j-i}2)\)。所以我们不妨找到所有 Runs \((l,r,p)\),显然以任意起点 \(i\in[l,l+2p-1)\) 都有 \(s[i:i+2p]\) 是一个本原串。

对于起点 \(i\) 我们设置 \(i+2kp,k\in\mathbb{Z}\) 作为二元组 \((i,p)\) 的关键点,那么任意两个关键点之间都是合法的本原串。

所以对于 \(f_i\) 我们统计 \(i\) 位置的关键点信息。同时对于每个二元组我们统计当前所有的 \(f_p\) 之和。由于本原平方串的性质,这样做是 \(O(n\log n)\) 的。

总复杂度 \(O(n\log n)\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<unordered_set>
#define ll long long
#define N 200010
#define mod 998244353
#define B 2333
using namespace std;
char str[N];
int s[N],n;
int h[N],bs[N];
int get(int l,int r){return (h[r]-1ll*h[l-1]*bs[r-l+1]%mod+mod)%mod;}
int lcp(int x,int y)
{
    int l=1,r=n-max(x,y)+1,res=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(get(x,x+mid-1)==get(y,y+mid-1)) l=mid+1;
        else r=mid-1;
    }
    return r;
}
int lcs(int x,int y)
{
    int l=1,r=min(x,y),res=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(get(x-mid+1,x)==get(y-mid+1,y)) l=mid+1;
        else r=mid-1;
    }
    return r;
}
bool cmp(int l1,int l2)//s[l1:]<s[l2:]
{
    int l=lcp(l1,l2);
    return s[l1+l]<s[l2+l];
}
int nxt[N];
int tt,pr[N*20],f[N],sf[N*20];
vector<int>g[N*20];
unordered_set<ll>fd;
void lyndon(bool ell)
{
    for(int i=n-1;i;i--)
        for(nxt[i]=i+1;nxt[i] && cmp(nxt[i],i)==ell;nxt[i]=nxt[nxt[i]]);
}
void make_runs()
{
    for(int k=n-1;k;k--)
    if(nxt[k])
    {
        int dl=lcs(k,nxt[k]),dr=lcp(k,nxt[k]),p=nxt[k]-k;
        int l=k-dl+1,r=nxt[k]+dr-1;
        if(dl+dr<=p || !fd.insert(1ll*r*N+l).second) continue;
        for(int i=l-1;i<l+2*p-1 && i+2*p<=r;i++)
        {
            int x=++tt;pr[x]=p;
            for(int j=i+2*p;j<=r;j+=2*p) g[j].push_back(x);
        }
    }
}
void init()
{
    bs[0]=1;
    for(int i=1;i<=n;i++) h[i]=(1ll*h[i-1]*B+s[i])%mod,bs[i]=1ll*bs[i-1]*B%mod;
}
int main()
{
    scanf("%s",str+1);
    n=strlen(str+1);
    for(int i=1;i<=n;i++) s[i]=str[i]-'a'+1;
    init();
    lyndon(0);make_runs();
    lyndon(1);make_runs();
    int sm=f[0]=1;
    for(int i=1;i<=n;i++)
    {
        f[i]=sm;
        for(int v:g[i]) f[i]=(f[i]-2ll*(sf[v]=(sf[v]+f[i-2*pr[v]])%mod)%mod+mod)%mod;
        sm=(sm+f[i])%mod;
    }
    printf("%d\n",f[n]);
    return 0;
}
posted @ 2020-11-24 20:43  Flying2018  阅读(377)  评论(0编辑  收藏  举报