牛客 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}\)

对应位置对应即可求解

posted @ 2021-08-09 22:12  JustinRochester  阅读(335)  评论(0编辑  收藏  举报