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

链接

毒瘤题。

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

然后考虑容斥,即“没有相同”看作 (sum_{k}(-1)^k ext{至少有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 imessum_{j=1}^{i-1}[R(s[i:j]) ext{ is even}]f_j ]

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

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

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

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

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

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

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

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

总复杂度 (O(nlog 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
",f[n]);
    return 0;
}
原文地址:https://www.cnblogs.com/Flying2018/p/14032526.html