CCPC Final 2023 B. Periodic Sequence

Zaunese發表於2024-11-06

https://vjudge.net/problem/QOJ-8543

給定 \(n\),對於 \(i=1,2,\ldots,n\) 求出最長可能的週期字串序列長度 F(i),滿足序列中字串的長度 \(≤i\)。一個字串序列 \(S_1,S_2,\ldots,S_l\) 是週期字串序列,當且僅
當對於每個 \(1≤i<l\) 都滿足 \(S_i\)\(S_{i+1}\) 的週期,並且它們兩兩不同。(n<=2e5)

先求單個 F(n)。

手玩幾下,發現:第一個串一定是長為 n 的,並且一定是 abbbb... 形式。

:abbbb
:abbb
 abbba
:abb
 abbab
 abba
 abbaa
:ab
 ababa
 abab
 aba
 abaab
 abaa
 abaaa
:a
 aa
 aaa
 aaaa
 aaaaa

觀察冒號處的字首最小值。後面拖了一段串。抓 ab 領導的這一段分析:

  1. 抓出 ab 後的每一個 a 開頭的串,形似整數拆分;
  2. 整數拆分是有序的;
  3. 拆分的整數都不能超過開頭 ab 串的長度(2)。
  4. 被拆分的整數不超過 n-|ab|。

即求序列 x 的個數,使得 \(\sum x\le n\)\(x_i\le x_1\)

列舉開頭 abb... 串的長度 x1=k,得 F(n) 的生成函式:

\[\frac1{1-x}\sum_{k\ge1}\frac{x^k}{1-\sum_{1\le i\le k}x^i}=\frac1{1-x}\sum_{k\ge1}\frac{x^k}{1-\frac{x-x^{k+1}}{1-x}} \]

外面乘的 \(\frac1{1-x}\) 是因為總和不超過 n 而不是恰好等於 n。

化簡:

\[\sum_{k\ge1}\frac{x^k}{1-(2x-x^{k+1})} \]

\(k\le\sqrt n\) 時,暴力計算貢獻。具體來說,不斷地給初始多項式 \(x^k\) 乘上 \((2x-x^{k+1})\),再加上 \(x^k\)。可以寫成完全揹包轉移。

\(k>\sqrt n\) 時,化原式:

\[\begin{aligned}&\frac{x^k}{(1-2x)(1-\frac{-x^{k+1}}{1-2x})}\\ =&\frac{x^k}{1-2x}\sum_{j\ge0}\left(\frac{-x^{k+1}}{1-2x}\right)^j\\ =&\sum_{j\ge0}(-1)^{j}\frac{x^{(k+1)j+k}}{(1-2x)^{j+1}} \end{aligned}\]

顯然 \(j\le\sqrt n\) 的是有用的。\(j\) 從大到小列舉,加完貢獻後乘 \(\frac1{1-2x}\),還是完全揹包轉移。

看 Dairuichen007 學會的程式碼。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

#define fi first
#define se second
#define mkp std::make_pair
using ll=long long;
using llu=unsigned long long;
using std::max;
using std::min;
template<class T> void cmax(T&a,T b){a=max(a,b);}
template<class T> void cmin(T&a,T b){a=min(a,b);}

const int NV=3e3;

ll mod;

namespace xm{
    int N,ans[NV+5],f[NV+5],g[NV+5];
    void lB(int k){
        memset(f,0,sizeof f);
        f[k]=1;
        for(int i=k+1;i<=N;++i) f[i]=(2ll*f[i-1]+mod-f[i-k-1])%mod;
        for(int i=1;i<=N;++i) ans[i]=(ans[i]+f[i])%mod;
    }void _(){
        scanf("%d%lld",&N,&mod);
        int B=sqrt(N);
        for(int i=1;i<=B;++i) lB(i);
        for(int x=B;~x;--x){
            for(int k=B+1;k<=N;++k){
                const int i=(k+1)*x+k;
                if(i<=N) g[i]=(g[i]+(x&1?mod-1:1))%mod;
            }
            for(int i=1;i<=N;++i) g[i]=(g[i]+2ll*g[i-1])%mod;
        }
        for(int i=1;i<=N;++i) ans[i]=(ans[i]+g[i])%mod;
        for(int i=1;i<=N;++i) printf("%lld ",ans[i]); puts("");
    }
}

int main(){
    xm::_();
    return 0;
}