第二類斯特林數

第二類斯特林數

斯特林數

講道理,斯特林數分爲兩類,遞推式子相似組合數,分別是$S(i,j)=S(i-1,j-1)+S(i-1,j)\times (i-1),S(i,j)=S(i-1,j-1)+j\times S(i-1,j)$php

(由於我不知道第一類有啥用,全部就直接上第二類...html

第二類斯特林數

對於上述表達式,是有意義的(廢話,否則我不會講node

這個東西,其實表達的東西很巧妙,就是:把n個不一樣的小球放在m個相同的盒子裏方案數。ios

記住上面的東西,這個是定義!這個是定義!這個是定義!less

根據上述的定義能夠獲得容斥原理的式子spa

$S(n,m)={\frac 1 {m!}}\sum_{k=0}^m (-1)^k C(m,k)(m-k)^n$code

道理是這樣的,枚舉空盒的個數,這樣就能夠容斥出剛好爲$K$的方案數...htm

而後一樣,經過定義能夠知道,$x^k=\sum\limits_{j=0}^kS(k,j)\times j!\times C(x,j)$blog

一樣,是表示,枚舉空盒數量,剩下來的劃分的方案數,這個東西顯然是給你$K$個球隨便放不用的方案數...get

上面的那個東西很重要,真的很重要!

由於能夠把$x^k$轉化爲$C(x,j)$,這樣就能夠用$C(x,j)=C(x-1,j-1)+C(x-1,j)$的性質了...

這類斯特林數顯然能夠經過推式子轉化爲卷積形式(本身推吧...

那麼能夠開始例題時間了...

例題時間

BZOJ 5093 [lydsy 1711 月賽] 圖的價值

這個東西就用到上面的性質了!

而後式子長成這個樣子:$ans=n\times 2^{C(n-1,2)}\sum\limits_{i=0}^{n-1}i^k C(n-1,i)$

而後顯然,就須要把$i^k$替換一下,$ans=n\times 2^{C(n-1,2)}\sum\limits_{i=0}^{n-1}C(n-1,i)\sum\limits_{j=0}^kS(k,j)\times j!\times C(i,j)$

設:$f(n,m)=\sum\limits_{i=0}^{n-1}C(n,i)\sum\limits_{j=0}^mS(m,j)\times j!\times C(i,j)$

那麼:$f(n,m)=\sum\limits_{j=0}^mj!\times S(m,j)\sum\limits_{i=0}^{n}C(n,i)C(i,j)$

根據定義展開組合數:$f(n,m)=\sum\limits_{j=0}^mj!\times S(m,j)\times C(n,j)\times 2^{n-j}$,顯然,由於後面的東西表示的是先在$n$個數中選$i$個而後再選$j$個的方案數,這個東西就等於再$n$個數中選$j$個,剩下$n-j$選不選都行的方案數...

這個東西顯然能夠算了...

只須要快速求出$S(m,j)$,這個東西你本身推去,我纔不寫呢...

代碼.jpg

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 262205
#define ll long long
#define mod 998244353
int fac[N],inv[N],n,k,A[N<<1],B[N<<1];
int q_pow(int x,int n){int ret=1;for(;n;n>>=1,x=(ll)x*x%mod)if(n&1)ret=(ll)ret*x%mod;return ret;}
int q_pow(int x,ll n){int ret=1;for(;n;n>>=1,x=(ll)x*x%mod)if(n&1)ret=(ll)ret*x%mod;return ret;}
#define inv(x) q_pow(x,mod-2)
void NTT(int *a,int len,int flag)
{
    int i,j,k,t,w,x,tmp;
    for(i=k=0;i<len;i++)
    {
        if(i>k)swap(a[i],a[k]);
        for(j=len>>1;(k^=j)<j;j>>=1);
    }
    for(k=2;k<=len;k<<=1)
    {
        x=q_pow(3,(mod-1)/k);if(flag==-1)x=inv(x);t=k>>1;
        for(i=0;i<len;i+=k)
            for(w=1,j=i;j<i+t;j++)
            {
                tmp=(ll)a[j+t]*w%mod;
                a[j+t]=(a[j]-tmp+mod)%mod;
                a[j]=(a[j]+tmp)%mod;w=(ll)w*x%mod;
            }
    }if(flag==-1)for(t=inv(len),i=0;i<len;i++)a[i]=(ll)a[i]*t%mod;
}
int main()
{
    scanf("%d%d",&n,&k);fac[0]=1;int len=1;while(len<=(k<<1))len<<=1;int ans=0;
    for(int i=1;i<=k;i++)fac[i]=fac[i-1]*(ll)i%mod;inv[k]=inv(fac[k]);
    for(int i=k;i;i--)inv[i-1]=inv[i]*(ll)i%mod;
    for(int i=0;i<=k;i++)A[i]=(i&1?mod-inv[i]:inv[i]);
    for(int i=0;i<=k;i++)B[i]=(ll)q_pow(i,k)*inv[i]%mod;
    NTT(A,len,1);NTT(B,len,1);for(int i=0;i<len;i++)A[i]=(ll)A[i]*B[i]%mod;NTT(A,len,-1);
    for(int i=0,C=1;i<=k;i++)
        ans=(ans+(ll)A[i]*fac[i]%mod*q_pow(2,n-i-1)%mod*C%mod*inv[i])%mod,C=(ll)C*(n-i-1)%mod;
    printf("%lld\n",(ll)ans*q_pow(2,(ll)(n-1)*(n-2)/2)%mod*n%mod);
}
[國家集訓隊] Crash的文明世界

這個題很是有趣...

一樣利用$x^k=\sum\limits_{j=0}^kS(k,j)\times j! \times C(x,j)$的性質...

顯然能夠講因此的$dis$轉化爲子樹內和子樹外。

那麼,設:$f(x)$表示$x$的子樹內貢獻,$g(x)$爲$x$的子樹外貢獻。

對應$(f(x))^k=\sum (f(to)+1)^k$

對應$(g(x))^k=(f(fa)+1)^k+(g(fa)+1)^k-(f(x)+2)^k$

之間暴力二項式大概須要跑100s左右。

而後能夠用斯特林數展開,發現前二者之間組合數加法就行,最後的那個須要展開兩次...

代碼.jpg

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 50005
#define M 155
#define mod 10007
#define ll long long
struct node{int to,next;}e[N<<1];
int s[M][M],f[N][M],g[N][M],n,K,head[N],cnt,siz[N],fac[M];
inline void add(int x,int y){e[cnt]=(node){y,head[x]};head[x]=cnt++;}
void dfs1(int x,int from)
{
    siz[x]=1;f[x][0]=1;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int to1=e[i].to;
        if(to1!=from)
        {
            dfs1(to1,x);f[x][0]=(f[x][0]+f[to1][0])%mod;int lim=min(siz[x],K);
            for(int j=1;j<=K;j++)f[x][j]=(f[x][j]+f[to1][j-1]+f[to1][j])%mod;
            siz[x]+=siz[to1];
        }
    }
}
void dfs2(int x,int from)
{
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int to1=e[i].to;
        if(to1!=from)
        {
            g[to1][0]=n-f[to1][0];
            for(int j=1;j<=K;j++)
            {
                g[to1][j]=((g[x][j]+g[x][j-1]+f[x][j-1]+f[x][j]-f[to1][j]-(f[to1][j-1]<<1))%mod+mod)%mod;
                if(j>1)g[to1][j]=(g[to1][j]-f[to1][j-2]+mod)%mod;
            }
            dfs2(to1,x);
        }
    }
}
int main()
{
    int L,A,B,Q,now;memset(head,-1,sizeof(head));
    scanf("%d%d%d%d%d%d%d",&n,&K,&L,&now,&A,&B,&Q);
    for(int i=1,x;i<n;i++)now=(now*A+B)%Q,x=i-now%min(i,L),add(x,i+1),add(i+1,x);
    s[0][0]=1;
    for(int i=1,j;i<=K;i++)for(j=1;j<=i;j++)s[i][j]=(s[i-1][j-1]+s[i-1][j]*j)%mod;
    fac[0]=1;
    for(int i=1;i<=K;i++)fac[i]=fac[i-1]*i%mod;dfs1(1,0);dfs2(1,0);
    for(int i=1;i<=n;i++)
    {
        int ans=0;
        for(int j=1;j<=K;j++)ans=(ans+s[K][j]*fac[j]%mod*(f[i][j]+g[i][j]))%mod;
        printf("%d\n",(ans+mod)%mod);
    }
}

CF 1097 G

CF 1097 G 題解

相關文章
相關標籤/搜索