牛客 11259 H Scholomance Academy 題解


傳送門

數學板子大綜合

數論+組合數學+多項式+線性遞推板子套板子題

干脆加上群論和線性代數湊個整好了


【大意】

規定 \(\displaystyle G(N)=\sum_{k_1+k_2+\cdots+k_t=N}\boldsymbol F(p_1^{k_1}p_2^{k_2}\cdots p_t^{k_t})\)

\(\displaystyle \boldsymbol F(n)=\sum_{a_1a_2\cdots a_m}\boldsymbol \varphi(a_1)\boldsymbol \varphi(a_2)\cdots \boldsymbol \varphi(a_m)\)

其中 \(\boldsymbol \varphi(n)\)\(n\) 的歐拉函數值

現給定 \(N, m, t, \{p_1, p_2,\cdots p_t\}\),求解 \(G(N)\bmod 998244353\)


【分析】

先從 \(F\) 下手,容易看出:

\(\displaystyle m=2\to \boldsymbol F(n)=\sum_{d\mid n}\boldsymbol \varphi(d)\boldsymbol \varphi({n\over d})\)

數學歸納法歸納一下,發現 \(\boldsymbol F(n)=\boldsymbol \varphi^m(n)\)

此處的乘法為狄利克雷卷積

考慮到 \(\displaystyle \boldsymbol \varphi(p^k)=p^k(1-{1\over p}), k>0\)\(\boldsymbol \varphi(1)=\boldsymbol \varphi(p^0)=1\)

所以有 \(\displaystyle \boldsymbol \varphi(p^k)=p^k(1-{1\over p})^{[k>0]}\)

由於 \(\boldsymbol F=\boldsymbol \varphi^m\) ,故 \(\boldsymbol F\) 為積性函數,我們考慮其 \(p^k\) 的值

因此 \(\displaystyle \boldsymbol F(p^k)=\sum_{i_1+i_2+\cdots +i_m=k}\boldsymbol \varphi(p^{i_1})\boldsymbol \varphi(p^{i_2})\cdots \boldsymbol \varphi(p^{i_m})\)

代入上式得 \(\displaystyle \boldsymbol F(p^k)=p^k \sum_{i_1+i_2+\cdots +i_m=k}(1-{1\over p})^{[i_1>0]+[i_2>0]+\cdots [i_m>0]}\)

考慮枚舉非零的個數 \(r\) ,剩下的 \((m-r)\) 個全部為 \(0\) ,等價於將 \(k\) 個完全一樣的小球放入 \(r\) 個完全一樣的盒子,方案數為 \(\dbinom{k-1}{r-1}\)

代入得 \(\displaystyle \boldsymbol F(p^k)=p^k \sum_{r=1}^m\dbinom m r \dbinom{k-1}{r-1}(1-{1\over p})^r,k>0\)


現考慮 \(\boldsymbol F\) 的積性,有:\(\displaystyle G(N)=\sum_{k_1+k_2+\cdots +k_m=N}\boldsymbol F(p_1^{k_1})\boldsymbol F(p_2^{k_2})\cdots \boldsymbol F(p_t^{k_t})\)

形式上非常像生成函數

我們記 \(\displaystyle G(x)=\sum_{n=0}^\infty G(n)x^n, F_i(x)=\sum_{n=0}^\infty F(p_i^n)x^n\)

\(\displaystyle G=\prod_{i=1}^t F_i\)

因此我們只需要考慮求解 \(F_i(x)\)

\(\displaystyle F_i(x)=\sum_{n=0}^\infty F(p_i^n)x^n=1+\sum_{n=1}^\infty F(p_i^n) x^n\)

代入上一節的 \(F(p^k)\) 公式得

\(\displaystyle F_i(x)=1+\sum_{n=1}^\infty x^np_i^n\sum_{r=1}^m\dbinom m r \dbinom{n-1}{r-1}(1-{1\over p})^r\)

無法求和,故更換求和順序,先求和 \(r\)

\(\displaystyle F_i(x)=1+\sum_{r=1}^m\dbinom m r (1-{1\over p})^r\sum_{n=1}^\infty x^np_i^n\dbinom{n-1}{r-1}\)

觀察式子:\(\displaystyle \sum_{n=1}^\infty x^np_i^n\dbinom{n-1}{r-1}\)

由於要 \(n\geq r\)\(\dbinom {n-1}{r-1}\) 才為正整數,且等於 \(\dbinom{n-1}{n-r}\)

故更改求和范圍得:

\(\displaystyle \sum_{n=1}^\infty x^np_i^n\dbinom{n-1}{r-1}=\sum_{n=r}^\infty x^np_i^n \dbinom{n-1}{n-r}=x^rp_i^r\sum_{n=r}^\infty x^{n-r}p_i^{n-r}\dbinom{(n-r)+r-1}{n-r}\)

將求和變量換元 \(n-r\to n\) 得:

\(\displaystyle \sum_{n=1}^\infty x^np_i^n\dbinom{n-1}{r-1}=x^rp_i^r\sum_{n=0}^\infty x^np_i^n\dbinom{n+r-1}{n}=x^rp_i^r\cdot {1\over (1-xp_i)^r}\)

故將結果代回生成函數得:

\(\displaystyle F_i(x)=1+\sum_{r=1}^m\dbinom m r (1-{1\over p_i})^rx^rp^r\cdot {1\over (1-xp_i)^r}=1+\sum_{r=0}^m \dbinom m r [(1-{1\over p_i})\cdot x\cdot p_i\cdot {1\over 1-xp_i}]^r\)

發現是二項式定理,所以

\(\displaystyle F_i(x)=1+(1+{p_ix-x\over 1-p_ix})^m-1=({1-x\over 1-p_ix})^m\)

所以代回生成函數,得到:

\(\displaystyle G(x)=\prod_{i=1}^tF_i(x)=(\prod_{i=1}^t {1-x\over 1-p_ix})^m\)


然而 \(n\leq 10^9\) ,無法直接跑多項式求解。

而且由於其最短遞推式長度 \(k\) 可能會達到 \(10^5\sim 10^6\) 級別,甚至無法用 BM+\(O(k^2\log n)\) 線性遞推求解

因此這邊需要用到其他方法求出最短遞推式,而后用 \(O(k\log k\log n)\) 的方法求解線性遞推

這邊先給出結論:若 \(\displaystyle G(x)={Q(x)\over P(x)}\) ,其中 \(Q(x),P(x)\) 分別是 \(\mu,m\) 次多項式

則對於 \((m+\mu)\) 及以內的系數,需要暴力求解

而對於 \(deg=(m+\mu+1)\) 及以上的系數,由長度為 \(m\) 的線性遞推求解

\((m+\mu)\) 及以內的系數無遞推關系,以上的有長度為 \(m\) 的線性遞推關系

該線性遞推關系為 \(\displaystyle g_n=-\sum_{i=1}^m p_ig_{n-i}\)

證明見下文


故我們先使用分治 NTT 或多項式啟發式合並,求解分母 \(P(x)\) ;用組合數展開求解分子 \(P(x)\)

然后對分母進行多項式取逆,暴力打出 \(G(x)\) 在模 \(x^{deg}\) 意義下的值

之后對於詢問進行分支:

對於 \(deg\) 范圍內的詢問 \(n\) ,直接輸出答案

對於 \(deg\) 范圍外的詢問 \(n\) ,丟進線性遞推直接求解

這里強調一下,由於遞推式從 \(deg\) 項開始有效,故遞推關系 \(\displaystyle g_n=-\sum_{i=1}^m p_ig_{n-i}\) 的實際最小項為 \(g_{deg-n}\)

而線性遞推的實際最小項為 \(g_0\)

因此需要對 \(n\) 減去偏移量 \((deg-n)\) ,計算線性遞推后,乘上的向量也應是從 \(g_{deg-n}\) 開始的;即向量也需要加上一個 \((deg-n)\) 的偏移量


【代碼】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
typedef double db;
typedef vector<int> vi;
#define fi first
#define se second
#define rep(i, a, b) for(int i=(a); i<(b); ++i)
#define per(i, a, b) for(int i=(b)-1;i>=(a);--i)
#define pb push_back
#define mp make_pair
#define sz(a) (int)a.size()
#define all(a) a.begin(), a.end()

const int LimBit=19, M = 1<<LimBit<<1, P=998244353;
int a[M], b[M], c[M], d[M], moda[M], modb[M];
vi T[M];
int n, t, m, f[M], g[M], h[M];
inline int add(int a, int b) { return (a+=b)>=P?a-P:a; }
inline int dis(int a, int b) { return (a-=b)<0?a+P:a; }
inline int mul(int a, int b) { return (ll)a*b%P; }
ll kpow(ll a, int b){
    ll c = 1;
    for (; b; b >>= 1,a = a * a % P) if (b & 1) c = c * a %P;
    return c;
}
struct NTT{
    int N, na, nb, W[2][M+M], Rev[M+M], *w[2], *rev, invN, InV[M];
    NTT(){
        w[0]=W[0], w[1]=W[1], rev=Rev;
        w[0][0]=w[1][0]=1;
        for(int i=1,x=kpow(3,P>>LimBit),y=kpow(x,P-2); !(i>>LimBit); ++i){
            rev[i]=(rev[i>>1]>>1)|((i&1)<<LimBit-1);
            w[0][i]=(ll)w[0][i-1]*x%P, w[1][i]=(ll)w[1][i-1]*y%P;
        }
        int *lstw[2]={w[0], w[1]};
        w[0]+=1<<LimBit, w[1]+=1<<LimBit, rev+=1<<LimBit;
        for(int d=LimBit-1;d>=0;--d){
            for(int i=0;!(i>>d);++i){
                rev[i]=(rev[i>>1]>>1)|((i&1)<<d-1);
                w[0][i]=lstw[0][i<<1], w[1][i]=lstw[1][i<<1];
            }
            lstw[0]=w[0], lstw[1]=w[1];
            w[0]+=1<<d, w[1]+=1<<d, rev+=1<<d;
        }
    }
    inline void work(){
        w[0]=W[0]; w[1]=W[1]; rev=Rev;
        for(int d=LimBit;1<<d>N;--d)
            w[0]+=1<<d, w[1]+=1<<d, rev+=1<<d;
        invN=kpow(N,P-2);
    }
    inline void FFT(int *a,int f){
        for(int i=0;i<N;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
        for(int i=1;i<N;i<<=1)
            for(int j=0,t=N/(i<<1);j<N;j+=i<<1)
                for(int k=0,l=0,x,y;k<i;k++,l+=t)
                    x=(ll)w[f][l]*a[j+k+i]%P, a[j+k+i]=dis(a[j+k],x), a[j+k]=add(a[j+k],x);
        if(f) for(int i=0;i<N;++i) a[i]=(ll)a[i]*invN%P;
    }
    inline void doit(int *a,int *b,int na,int nb){//3*NTT
        for(N=1;N<na+nb-1;N<<=1);
        for(int i=na;i<N;++i) a[i]=0;
        for(int i=nb;i<N;++i) b[i]=0;
        work(); FFT(a,0); FFT(b,0);
        for(int i=0;i<N;++i) a[i]=(ll)a[i]*b[i]%P;
        FFT(a,1);
    }
    inline void get_inv(int *f,int *g,int n){//6*NTT
        g[0]=kpow(f[0],P-2);
        int l;
        for(l=1;l<n;l<<=1){
            for(int i=0;i<l;++i) a[i]=f[i];
            for(int i=l;i<l<<1;++i) a[i]=f[i],g[i]=0;
            N=l<<2;
            for(int i=l<<1;i<N;++i) a[i]=g[i]=0;
            work(); FFT(a,0); FFT(g,0);
            for(int i=0;i<N;++i) g[i]=(ll)g[i]*dis(2,(ll)g[i]*a[i]%P)%P;
            FFT(g,1);
        }
        for(int i=n;i<N;++i) g[i]=0;
    }
    priority_queue<pii> H;
    inline void Merge(int cnt){
        while(H.size()) H.pop();
        for(int i=1;i<=cnt;++i) H.emplace( pii(-T[i].size(), i) );
        while(H.size()>=2){
            int x=H.top().se; H.pop();
            int y=H.top().se; H.pop();
            for(int i=0;i<T[x].size();++i) a[i]=T[x][i];
            for(int i=0;i<T[y].size();++i) b[i]=T[y][i];
            doit(a, b, T[x].size(), T[y].size());
            int na=T[x].size()+T[y].size();
            T[x].clear(); T[y].clear();
            for(int i=0;i<na;++i) T[x].push_back(a[i]);
            H.emplace( pii(-T[x].size(), x) );
        }
        swap(T[H.top().se], T[1]);
        H.pop();
    }
    inline void get_mod(int *f,int *g,int n,int m){
        while(n>=0&&!f[n-1]) --n;
        if(n<m) return ;
        reverse(g, g+m);
        get_inv(g, modb, n-m+1);
        reverse(g, g+m);
        for(int i=0;i<n;++i) moda[i]=f[i];
        reverse(moda, moda+n);
        doit(moda, modb, n-m+1, n-m+1);
        reverse(moda, moda+n-m+1);
        for(int i=0;i<m;++i) modb[i]=g[i];
        doit(moda, modb, n-m+1, m);
        for(int i=0;i<n;++i) f[i]=dis(f[i], moda[i]);
    }
    inline void get_xpow(int *g,int *m,int x,int nm){
	if(x==0){
	    g[0]=1;
	    return ;
	}
	get_xpow(g, m, x>>1, nm);
	for(N=1;N<nm+nm-3;N<<=1);
	for(int i=nm-1;i<N;++i) g[i]=0;
	work(); FFT(g, 0);
	for(int i=0;i<N;++i) g[i]=(ll)g[i]*g[i]%P;
	FFT(g, 1);
	if(x&1){
	    for(int i=N;i>=1;--i) g[i]=g[i-1];
    	    g[0]=0;
    	    get_mod(g, m, N+1, nm);
	}
	else get_mod(g, m, N, nm);
	for(N=1;N<nm+nm-3;N<<=1);
	for(int i=nm-1;i<N;++i) g[i]=0;
    }
} ntt;

const int MAXN=1e6+1;
int Inv[MAXN], deg;
inline void init(){
    cin>>n>>t>>m;
    for(int i=1, p;i<=t;++i){
        cin>>p;
        T[i].push_back(1);
        T[i].push_back( dis(0, p) );
    }
    ntt.Merge(t);
    for(int i=2;i<=m;++i) T[i]=T[1];
    ntt.Merge(m);

    deg=T[1].size()+m*t;
    for(int i=0;i<deg;++i) g[i]=(i<T[1].size()?T[1][i]:0);
    ntt.get_inv(g, f, deg);
    Inv[1]=1;
    for(int i=2;i<MAXN;++i)
        Inv[i]=P-(ll)Inv[P%i]*(P/i)%P, g[i]=0;
    for(int i=0, sgn=0, comb=1, I=m*t;i<=I;++i, sgn^=1){
        if(sgn) g[i]=dis(0, comb);
        else g[i]=comb;
        comb=(ll)comb*(I-i)%P*Inv[i+1]%P;
    }
    ntt.doit(f, g, deg, m*t+1);
}

struct LinearRecurrence{
    int k, g[M], c[M];

    inline void pre(const vector<int> &f) {
        k=f.size()-1;
        for(int i=0;i<k;++i) g[i]=f[k-i];
        g[k]=1;
    }
    inline int ans(int *a, int n) {
        ntt.get_xpow(c, g, n, k+1);
        int res=0;
        for(int i=0;i<k;++i)
            res=add(res, (ll)c[i]*a[i]%P);
        return res;
    }
    inline int work(int *a, int n){
        int offset=deg-k;
        if(n<deg) return a[n];
        else return ans(a+offset, n-offset);
    }
}LR;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    init();
    LR.pre(T[1]);
    cout<<LR.work(f, n);
    cout.flush();
    return 0;
}

【證明】

感謝 Stump 貢獻的方向

\(\displaystyle P(x)=\sum_{n=0}^m p_nx^n, Q(x)=\sum_{n=0}^\mu q_nx^n\)

\(\displaystyle R(x)={1\over P(x)}=\sum_{n=0}^\infty r_nx^n\)

\(\displaystyle 1=P(x)\cdot R(x)\)\(\displaystyle \forall n> m\to \sum_{i=0}^m p_ir_{n-i}=\sum_{i+j=n}p_ir_j=0\)

\(\displaystyle G(x)={Q(x)\over P(x)}=Q(x)\cdot R(x)\)\(\displaystyle g_n=\sum_{i+j=n}q_ir_j\)

\(\displaystyle \forall n\geq m+\mu+1\to \sum_{i=0}^m p_ig_{n-i}=\sum_{i+j+k=n}p_iq_jr_k=\sum_{j=0}^\mu q_j\sum_{i+k=n-j}p_ir_k\)

由於 \(n-j\geq n-\mu \geq m+1>m\)\(\displaystyle \sum_{i+k=n-j}p_ir_k=0\) 恆成立

因此 \(\displaystyle \sum_{i=0}^m p_ig_{n-i}=\sum_{j=0}^\mu q_j\sum_{i+k=n-j}p_ir_k=0\)\(n\geq m+\mu+1\) 時恆成立

故移項得 \(\displaystyle g_n=-{1\over p_0}\sum_{i=1}^m p_ig_{n-i}\)

考慮到本題中 \(\displaystyle P(x)=\prod_{i=1}^t(1-p_ix)^m\to p_0=1\) 以及線性遞推的形式 \(\displaystyle a_n=\sum_{i=1}^k f_ia_{n-i}\)

對應位置對應即可求解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM