【CTS2019】珍珠(生成函數)

【CTS2019】珍珠(生成函數)

題面

LOJ
洛谷ios

題解

lun題可海星。
首先一個大暴力\(sb\)\(dp\)是設\(f[i][S]\)表示當前考慮完了前\(i\)個珍珠,\(S\)集合中這些顏色的珍珠當前還有一個沒有匹配。這個隨便轉移就好了。
而後發現並無任何須要記錄下確切的哪些顏色是奇數個,只須要記錄有多少種就好了。
這樣子能夠作到\(O(nd)\)
從這裏咱們看出,最終可以匹配出來的對數剛好等於\((n-|S|)/2\),總個數減去奇數顏色的個數的一半。
首先若是咱們可以知道每種顏色的奇偶狀況,那麼每一個顏色均可以寫成一個指數型生成函數,而後所有乘起來的第\(n\)項就是答案。
其中,奇數的生成函數是:\(A(x)=\frac{e^x-e^{-x}}{2}\),偶數的生成函數是:\(B(x)=\frac{e^x+e^{-x}}{2}\)
那麼咱們假設枚舉出來有\(p\)個顏色是奇數,方案數就是\([x^n]A^p(x)B^{d-p}(x)\)
因此答案式:
\[\begin{aligned} ans&=\frac{n!}{2^d}\sum_{p=0}^{n-2m}[x^n]{d\choose p}(e^x-e^{-x})^p(e^x+e^{-x})^P\\ &=\frac{n!}{2^d}\sum_{p=0}^{n-2m}[x^n]{d\choose p}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}{p\choose i}{d-p\choose j}e^{(2i+2j-d)x}\\ &=\frac{1}{2^d}\sum_{p=0}^{n-2m}{d\choose p}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}{p\choose i}{d-p\choose j}(2i+2j-d)^n\\ &=\frac{1}{2^d}d!\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}\frac{(-1)^{p-i}(2i+2j-d)^n}{i!(p-i)!j!(d-p-j)!} \end{aligned}\]
這樣子直接暴力能夠作到\(O(d^3)\)
考慮接着往下進行一些變化,首先咱們發現咱們的這個式子麻煩在\(i,j\)既有分開的項,又有\(i+j\)的項,那麼把這兩個部分給分開看。
\[\begin{aligned} ans&=\frac{1}{2^d}d!\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}\frac{(-1)^{p-i}(2i+2j-d)^n}{i!(p-i)!j!(d-p-j)!}\\ &=\frac{1}{2^d}d!\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}(2(i+j)-d)^n\frac{1}{i!j!}\frac{1}{(p-i)!(d-p-j)!}\\ &=\frac{1}{2^d}d!\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}\frac{(2(i+j)-d)^n}{(i+j)!(d-(i+j))!}\frac{(i+j)!}{i!j!}\frac{(d-(i+j))!}{(p-i)!(d-p-j)!}\\ &=\frac{1}{2^d}d!\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}\frac{(2(i+j)-d)^n}{(i+j)!(d-(i+j))!}{i+j\choose i}{d-(i+j)\choose p-i}\\ &=\frac{1}{2^d}\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}(2(i+j)-d)^n{d\choose i+j}{i+j\choose i}{d-(i+j)\choose p-i}\\ \end{aligned}\]函數

這樣子再往下我彷佛就不會推了,難受。
\(memset0\)的作法能夠在洛谷的題解內看到,我這裏大體的複述一下。
考慮把後面的組合數轉變爲格路問題,\(i+j\choose i\)是從\((0,0)\)走到\((i,j)\)的方案數,\(d-(i+j)\choose p-i\)是從\((i,j)\)走到\((p,d-p)\)的方案數。
而後這裏還有一個\((-1)^{p-i}\)的貢獻,格路這個東西也能夠寫成一個生成函數,既然做爲楊輝三角的一層,因此生成函數就是\((1+x)^d\)的形式。
而後這裏枚舉的時候並無單獨出現過\(j\)了,因此只須要枚舉\(i+j\)會更加方便。
\[\begin{aligned} ans&=\frac{1}{2^d}\sum_{i=0}^d(2i-d)^n{d\choose i}\sum_{p=0}^{n-2m}[x^p](1+x)^i(1-x)^{d-i} \end{aligned}\]
講講這個爲何是對的,首先\((-1)^{p-i}\)表示只和第二次走的距離相關,而橫座標總共要走\(p\)步,第一次走不會產生符號,因此就是\((1+x)^i\),第二次會產生符號,之和走的橫座標的步數相關,奇數步是\(-1\),偶數步是\(1\),因此是\((1-x)^{d-i}\)
那麼令\(F(i,d)=\sum_{p=0}^{n-2m}[x^p](1+x)^i(1-x)^{d-i}\)
因此有
\[\begin{aligned} ans&=\frac{1}{2^d}\sum_{i=0}^d(2i-d)^n{d\choose i}\sum_{p=0}^{n-2m}[x^p](1+x)^i(1-x)^{d-i}\\ &=\frac{1}{2^d}\sum_{i=0}^d(2i-d)^n{d\choose i}F(i,d) \end{aligned}\]
考慮怎麼計算\(F\),那麼考慮能不能遞推。
首先邊界\(F(0,0)=1\)
\[\begin{aligned} F(0,d)&=\sum_{k=0}^{n-2m}[x^k](1-x)^d\\ &=\sum_{k=0}^{n-2m}{d\choose k}(-1)^k\\ &=\sum_{k=0}^{n-2m}(-1)^k({d-1\choose k}+{d-1\choose k-1})\\ &=\sum_{k=0}^{n-2m}(-1)^k{d-1\choose k}+\sum_{k=0}^{n-2m-1}(-1)^{k+1}{d-1\choose k}\\ &=(-1)^{n-2m}{d-1\choose n-2m} \end{aligned}\]
而後考慮其餘的狀況,
\[\begin{aligned} F(i,d)&=\sum_{k=0}^{n-2m}[x^k](1-x)^{d-i}(1+x)^i\\ &=\sum_{k=0}^{n-2m}[x^k](1+x)(1+x)^{i-1}(1-x)^{d-i}\\ &=\sum_{k=0}^{n-2m}[x^k](-(1-x)+2)(1+x)^{i-1}(1-x)^{d-i}\\ &=-\sum_{k=0}^{n-2m}[x^k](1+x)^{i-1}(1-x)^{d-i+1}+2\sum_{k=0}^{n-2m}[x^k](1+x)^{i-1}(1-x)^{d-i}\\ &=-F(i-1,d)+2F(i-1,d-1) \end{aligned}\]
因而這個東西肉眼可見的能夠繼續在網格圖上走,即有兩種走法,一種是沿着橫座標走一步,另一種是兩個座標一塊兒走一步。
發現不管如何橫座標都要走,因此惟一須要決策的只有豎座標的移動,而兩種移動方式分別產生\(-1\)\(2\)的貢獻,那麼咱們枚舉起點就能夠知道豎座標要走多少步,而後組合數就能夠隨便算了。
因此獲得:
\[F(i,d)=\sum_{j=0}^d F(0,j){i\choose d-j}(-1)^{i+j-d}2^{d-j}\]
這樣子愉快的把答案式給展開:
\[\begin{aligned} ans&=\frac{1}{2^d}\sum_{i=0}^d(2i-d)^n{d\choose i}F(i,d)\\ &=\frac{1}{2^d}\sum_{i=0}^d(2i-d)^n{d\choose i}\sum_{j=0}^d F(0,j){i\choose d-j}(-1)^{i+j-d}2^{d-j}\\ &=\frac{d!}{2^d}\sum_{i=0}^d\sum_{j=0}^d \frac{(-1)^{d-i-j}}{(i+j-d)!}\frac{(2i-d)^n}{(d-i)!}\frac{F(0,j)2^{d-j}}{(d-j)!} \end{aligned}\]
後面兩個東西能夠處理成一個卷積,而前面那一項剛好只和\(i+j\)相關,那麼直接\(NTT\)乘起來而後枚舉一下就能算了。spa


然而這樣子的推導很麻煩,lun課件中的方法是一致的,
可是中間推導的部分更加簡單。
不同的地方在於
\[ans=\frac{n!}{2^d}\sum_{p=0}^{n-2m}[x^n]{d\choose p}(e^x-e^{-x})^p(e^x+e^{-x})^P\]
本來這裏把奇偶分開而後統一的計算\([x^n]\),那麼如今寫成這樣子:
\[[x^n][y^p](y(e^x-e^{-x})+(e^x+e^{-x}))^d\]
這樣本身就一個式子欽定了奇數被選的次數。
也就是:
\[\begin{aligned} ans&=\frac{n!}{2^d}\sum_{p=0}^{n-2m}[x^n][y^p](y(e^x-e^{-x})+(e^x+e^{-x}))^d\\ &=\frac{n!}{2^d}\sum_{p=0}^{n-2m}[x^n][y^p](e^x(1+y)+e^{-x}(1-y))^d\\ &=\frac{n!}{2^d}\sum_{p=0}^{n-2m}\sum_{i=0}^d e^{(2i-d)x}(1+y)^i(1-y)^{d-i}[x^n][y^p]\\ &=\frac{n!}{2^d}\sum_{p=0}^{n-2m}(2i-d)^n \sum_{i=0}^d((1+y)^i(1-y)^{d-i})[y^p] \end{aligned}\]
而後令\(F(i,d)\)爲後面那個\(\sum\),就和前面的推導一致了。
可是這樣子方便多了,不須要再轉化成格路來進行推導。code


而後還有\(1t5t\)的方法,
咱們只考慮枚舉奇數至少有多少個,這樣子咱們枚舉完奇數個數以後剩下的隨意,也就是\(e^{(d-p)x}\)
那麼設\(f_i\)表示強制有\(i\)個爲奇數的方案數,
有:
\[\begin{aligned} f_i&=[x^n]{d\choose i}(\frac{e^x-e^{-x}}{2})^ie^{(d-i)x}\\ &=[x^n]\frac{1}{2^i}\frac{d!}{i!(d-i)!}e^{(d-i)x}\sum_{j=0}^i {i\choose j}(-1)^j e^{(i-2j)x}\\ &=[x^n]\frac{1}{2^i}\frac{d!}{i!(d-i)!}\sum_{j=0}^i {i\choose j}(-1)^j e^{(d-2j)x}\\ &=\frac{1}{2^i}\frac{d!}{i!(d-i)!}\sum_{j=0}^i \frac{i!}{j!(i-j)!}(-1)^j (d-2j)^n \end{aligned}\]
這樣子就能夠卷積算出全部的\(f\),而後用二項式反演去求解全部的剛好而後計算答案了。get


代碼是前兩種作法的。it

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define MAX 550550
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
int W[MAX],P[MAX],r[MAX];
void NTT(int *P,int len,int opt)
{
    int l=0,N;for(N=1;N<len;N<<=1)++l;
    for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
    for(int i=1;i<N;i<<=1)
    {
        int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
        for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
        for(int j=0,p=i<<1;j<N;j+=p)
            for(int k=0;k<i;++k)
            {
                int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
                P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
            }
    }
    if(opt==-1)
    {
        reverse(&P[1],&P[N]);
        for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
    }
}
int jc[MAX],jv[MAX],inv[MAX];
int C(int n,int m){if(n<0||m<0||n<m)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int A[MAX],B[MAX],F[MAX];
int D,n,m,ans;
int main()
{
    D=read();n=read();m=read();
    jc[0]=jv[0]=inv[0]=inv[1]=1;
    for(int i=2;i<=D;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    for(int i=1;i<=D;++i)jc[i]=1ll*jc[i-1]*i%MOD;
    for(int i=1;i<=D;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
    for(int i=0;i<=D;++i)A[i]=1ll*fpow(((i+i-D)%MOD+MOD)%MOD,n)*jv[D-i]%MOD;
    for(int i=1;i<=D;++i)F[i]=1ll*((n&1)?MOD-1:1)%MOD*C(i-1,n-2*m)%MOD;F[0]=1;
    for(int i=0;i<=D;++i)B[i]=1ll*fpow(2,D-i)*jv[D-i]%MOD*F[i]%MOD;
    int N;for(N=1;N<=D+D;N<<=1);
    NTT(A,N,1);NTT(B,N,1);
    for(int i=0;i<N;++i)A[i]=1ll*A[i]*B[i]%MOD;
    NTT(A,N,-1);
    for(int i=D;i<=D+D;++i)ans=(ans+1ll*(((D+i)&1)?MOD-1:1)*jv[i-D]%MOD*A[i])%MOD;
    ans=1ll*ans*jc[D]%MOD*fpow(fpow(2,D),MOD-2)%MOD;
    printf("%d\n",ans);
    return 0;
}
相關文章
相關標籤/搜索