背景:
好久沒有更,最近都在準備模擬(水 )賽。
第一類斯特林數:
第一類斯特林數第行列,記做。
:
題目傳送門:https://www.luogu.org/problem/P5408。
現在你有個珠子,每一個珠子的顏色各不相同,求這些珠子組成個項鍊的方案數。
現在求時方案數。
:
設表示上面的方案數。
那麼有:
解釋一下:對於當前的珠子來說,有兩種選擇:
:新開一個項鍊,那麼方案數就要繼承;
:在原來的項鍊里加入,考慮它會加在某一個珠子的後面,由於之前已經有個珠子,可以加在任意一個後面,因此方案數爲。
可是這是的大暴力啊。
第一類斯特林數有一個性質就是它可以表示下面的形式:
現在這個式子就可以分治解決了。
時間複雜度:。
而模板題卡這種做法,因此不能通過。
有的做法,可我太菜了,不想學。
:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
const LL mod=167772161,G=3,inv_G=55924054;
using namespace std;
int r[800010],f[800010],a[20][800010],b[20][800010];
int n,A,B,l,limit;
int dg(int x,int k)
{
if(!k) return 1;
int op=dg(x,k>>1);
if(k&1) return (LL)op*op%mod*x%mod; else return (LL)op*op%mod;
}
int get_inv(int x)
{
return dg(x,mod-2);
}
void init(int n)
{
limit=1,l=0;
while(limit<(n<<1))
limit<<=1,l++;
for(int i=1;i<limit;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
}
void NTT(int *now,int limit,int op)
{
for(int i=0;i<limit;i++)
if(i<r[i]) swap(now[i],now[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
int wn=dg(op==1?G:inv_G,(mod-1)/(mid<<1));
for(int j=0;j<limit;j+=(mid<<1))
{
int w=1;
for(int k=0;k<mid;k++,w=((LL)w*wn)%mod)
{
int x=now[j+k],y=(LL)w*now[j+k+mid]%mod;
now[j+k]=(x+y)%mod;
now[j+k+mid]=(x-y+mod)%mod;
}
}
}
if(op==1) return;
int INV=get_inv(limit);
for(int i=0;i<limit;i++)
now[i]=(LL)now[i]*INV%mod;
}
void poly_CDQ(int *f,int dep,int l,int r)
{
if(l==r)
{
f[0]=l;f[1]=1;
return;
}
int mid=(l+r)>>1;
init(r-l+1+1);
for(int i=0;i<limit;i++)
a[dep][i]=b[dep][i]=0;
poly_CDQ(a[dep],dep+1,l,mid),poly_CDQ(b[dep],dep+1,mid+1,r);
init(r-l+1+1);
NTT(a[dep],limit,1),NTT(b[dep],limit,1);
for(int i=0;i<limit;i++)
f[i]=(LL)a[dep][i]*b[dep][i]%mod;
NTT(f,limit,-1);
}
int main()
{
scanf("%d",&n);
poly_CDQ(f,0,0,n-1);
for(int i=0;i<=n;i++)
printf("%d ",f[i]);
}
第二類斯特林數:
第二類斯特林數第行列,記做。
這一個更加廣泛應用。
:
題目傳送門:https://www.luogu.org/problem/P5395 。
現在你有個珠子,每一個珠子的顏色各不相同,求這些珠子放進個盒子的方案數。
現在求時方案數。
:
設表示上面的方案數。
那麼有:
解釋一下:對於當前的珠子來說,有兩種選擇:
:新開一個盒子,那麼方案數就要繼承;
:在原來的盒子裏加入,考慮它會加在某一個盒子裏,由於之前已經有個盒子,可以加在任意一個裏面,因此方案數爲。
可是這是的大暴力啊。
第二類斯特林數有一個性質就是它可以表示下面的形式:
爲什麼呢?
假設每一個盒子都不同,並且我們允許空盒的存在。
但是我們當然就是不允許空盒存在啊。
所以考慮容斥。
枚舉我當前有個空盒子。
那麼先把這個盒子選出來,也就是。
然後剩下個盒子,個球可以隨便放,也就是。
考慮到我們盒子是沒有區別的,所以除以。
現在這個式子是一個卷積的形式,就可以解決了。
時間複雜度:。
:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
const int mod=167772161,G=3,inv_G=55924054;
using namespace std;
int a[1000010],b[1000010],f[1000010],g[1000010],inv[1000010],Inv[1000010];
int limit,n,l,r[1000010];
int dg(int x,int k)
{
if(!k) return 1;
int op=dg(x,k>>1);
if(k&1) return (LL)op*op%mod*x%mod; else return (LL)op*op%mod;
}
int get_inv(int x)
{
return dg(x,mod-2);
}
void init(int n)
{
limit=1,l=0;
while(limit<(n<<1))
limit<<=1,l++;
for(int i=1;i<limit;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
}
void NTT(int *now,int limit,int op)
{
for(int i=0;i<limit;i++)
if(i<r[i]) swap(now[i],now[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
int wn=dg(op==1?G:inv_G,(mod-1)/(mid<<1));
for(int j=0;j<limit;j+=(mid<<1))
{
int w=1;
for(int k=0;k<mid;k++,w=((LL)w*wn)%mod)
{
int x=now[j+k],y=(LL)w*now[j+k+mid]%mod;
now[j+k]=(x+y)%mod;
now[j+k+mid]=(x-y+mod)%mod;
}
}
}
}
void dft(int *f,int n,int limit)
{
NTT(f,limit,-1);
int INV=get_inv(limit);
for(int i=0;i<n;i++)
f[i]=(LL)f[i]*INV%mod;
}
void Init()
{
inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=((LL)mod-mod/i)*inv[mod%i]%mod;
Inv[0]=Inv[1]=1;
for(int i=2;i<=n;i++)
Inv[i]=(LL)Inv[i-1]*inv[i]%mod;
}
int main()
{
scanf("%d",&n);
Init();
for(int i=0;i<=n;i++)
{
f[i]=(((i&1)?-1ll:1ll)*Inv[i]+mod)%mod;
g[i]=(LL)dg(i,n)*Inv[i]%mod;
}
init(n+1);
NTT(f,limit,1),NTT(g,limit,1);
for(int i=0;i<limit;i++)
f[i]=(LL)f[i]*g[i]%mod;
dft(f,n+1,limit);
for(int i=0;i<=n;i++)
printf("%d ",f[i]);
}
貝爾數:
:
現在你有個珠子,每一個珠子的顏色各不相同,求這些珠子放進若干個盒子的方案數。
現在求時方案數。
:
枚舉盒子數,顯然我們有:
可是這樣的時間複雜度是的,而且還要預處理第二類斯特林數,極其複雜。
考慮它的遞推式:
理解起來也不難,枚舉珠子數,考慮從個珠子裏選出個珠子放在一個盒子裏,那麼
未完待續。
後記:
有什麼好的套路或性質以後再補充吧,畢竟還沒有做題。
歡迎大佬指出錯誤。
一些性質:
持續更哦。