多项式
多项式全家桶
多项式
定义
形如
对于如
运算
加减运算
两个多项式
卷积(乘法)
两个多项式
FFT
单位根
对于
在弧度制下,该角弧度为
那么可以推导出:
DFT
考虑将这
我们可以发现,
IDFT
现在已知
假设有个向量
那么:
总结
那么两个多项式相乘可以先将其分别转化为点值表示法,然后对于位置相乘得到结果的点值表示法,然后在跑一遍那个将多项式转化为点值的函数,将点值转化为多项式,最后依据
#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define mk make_pair
#define reaD read
#define raed read
#define haed head
#define cotu cout
#define se second
#define fi first
#define itn int
using namespace std;
bool Mst;
const int Max=3e6+10;
const int mod=998244353;
const int inf=1e9+10;
const double PI=acos(-1.0);
inline int read(){
int res=0,v=1;
char c=getchar();
while(c<'0'||c>'9'){v=(c=='-'?-1:1);c=getchar();}
while(c>='0'&&c<='9'){res=(res<<3)+(res<<1)+(c^48);c=getchar();}
return res*v;
}
struct comple{
double a,b;
comple(double a=0,double b=0):
a(a),b(b){;}
comple operator +(const comple &x)const{return comple(a+x.a,b+x.b);}
comple operator -(const comple &x)const{return comple(a-x.a,b-x.b);}
comple operator *(const comple &x)const{return comple(a*x.a-b*x.b,a*x.b+b*x.a);}
};
void Fast(int now,comple *a,int tag){
if(now==1)return;
comple a1[(now>>1)+10],a2[(now>>1)+10];
for(int i=0;i<(now>>1);++i){
a1[i]=a[i<<1];
a2[i]=a[i<<1|1];
}
Fast(now>>1,a1,tag);
Fast(now>>1,a2,tag);
comple W=comple(cos(2.0*PI/now),tag*(sin(2.0*PI/now))),w=comple(1,0);
for(int i=0;i<(now>>1);++i,w=w*W){
comple z=w*a2[i];
a[i]=a1[i]+z;
a[i+(now>>1)]=a1[i]-z;
}
}
comple a[Max],b[Max];
bool Med;
signed main(){
int n=read();
int m=read();
for(int i=0;i<=n;++i)a[i].a=raed();
for(int i=0;i<=m;++i)b[i].a=read();
int pos=1;
while(pos<=n+m)pos<<=1;
Fast(pos,a,1);
Fast(pos,b,1);
for(int i=0;i<=pos;++i)a[i]=a[i]*b[i];
Fast(pos,a,-1);
for(int i=0;i<=n+m;++i)cout << (int)(a[i].a/pos+0.5)<< ' ';
cerr<< "Time: "<<clock()/1000.0 << "s\n";
cerr<< "Memory: " << (&Mst-&Med)/1000000.0 << "MB\n";
return 0;
}
实际上对于上述算法还可以进行优化,就是不递归的写法。
因为我们发现一个数的位置是其元下标二进制翻转后的位置,所有可以预处理然后枚举长度合并。
#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define mk make_pair
#define reaD read
#define raed read
#define haed head
#define cotu cout
#define se second
#define fi first
#define itn int
using namespace std;
bool Mst;
const int Max=3e6+10;
const int mod=998244353;
const int inf=1e9+10;
const double PI=acos(-1.0);
inline int read(){
int res=0,v=1;
char c=getchar();
while(c<'0'||c>'9'){v=(c=='-'?-1:1);c=getchar();}
while(c>='0'&&c<='9'){res=(res<<3)+(res<<1)+(c^48);c=getchar();}
return res*v;
}
struct comple {
double a,b;
comple (double a=0,double b=0):
a(a),b(b){;}
comple operator +(const comple &x)const{return comple(a+x.a,b+x.b);}
comple operator -(const comple &x)const{return comple(a-x.a,b-x.b);}
comple operator *(const comple &x)const{return comple(a*x.a-b*x.b,a*x.b+b*x.a);}
};
int rev[Max];
int limit;
void Fast(comple *a,int tag){
for(int i=0;i<limit;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int mid=1;mid<limit;mid<<=1){
comple W=comple(cos(PI/mid),tag*sin(PI/mid));
for(int R=mid<<1,j=0;j<limit;j+=R){
comple w=comple(1,0);
for(int l=0;l<mid;++l,w=w*W){
comple x=a[l+j],y=a[l+j+mid]*w;
a[l+j]=x+y;
a[l+j+mid]=x-y;
}
}
}
}
comple a[Max],b[Max];
bool Med;
signed main(){
int n=read();
int m=read();
for(int i=0;i<=n;++i)a[i].a=read();
for(int i=0;i<=m;++i)b[i].a=read();
limit=1;
int len=0;
while(limit<=n+m)limit<<=1,++len;
for(int i=0;i<=limit;++i)rev[i]=((rev[i>>1]>>1)|((i&1)<<(len-1)));
Fast(a,1);
Fast(b,1);
for(int i=0;i<=limit;++i)a[i]=a[i]*b[i];
Fast(a,-1);
for(int i=0;i<=n+m;++i)cout << (int)(a[i].a/limit+0.5) << ' ';
cout << "\n";
cerr<< "Time: "<<clock()/1000.0 << "s\n";
cerr<< "Memory: " << (&Mst-&Med)/1000000.0 << "MB\n";
return 0;
}
NTT
原根
将最小的满足
如果
求原根可以这样做:如果
可以发现原根有一个非常优秀的性质就是对于在
那么现在我们可以用
#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define mk make_pair
#define reaD read
#define raed read
#define haed head
#define cotu cout
#define se second
#define fi first
#define itn int
using namespace std;
bool Mst;
const int Max=3e6+10;
const int mod=998244353;
const int inf=1e9+10;
const int g=3;
const int invg=332748118;
template <int mod>
struct modint{
int val;
static int norm(const int &x){return x<0?x+mod:x;}
modint inv()const{
int a=val,b=mod,u=1,v=0,t;
while(b>0)t=a/b,swap(a-=t*b,b),swap(u-=t*v,v);
return modint(u);
}
modint():val(0){}
modint(const int &m):val(norm(m)){}
modint(const ll &m):val(norm(m%mod)){}
modint operator -()const{return modint(norm(-val));}
bool operator ==(const modint &x){return val==x.val;}
bool operator !=(const modint &x){return val!=x.val;}
bool operator <=(const modint &x){return val<=x.val;}
bool operator >=(const modint &x){return val>=x.val;}
bool operator >(const modint &x){return val>x.val;}
bool operator <(const modint &x){return val<x.val;}
modint& operator *=(const modint &x){return val=static_cast<int>(1ll*val*x.val%mod),*this;}
modint& operator +=(const modint &x){return val=(1ll*val+x.val)%mod,*this;}
modint& operator -=(const modint &x){return val=norm(1ll*val-x.val),*this;}
modint& operator >>=(const modint &x){return val>>=x.val,*this;}
modint& operator <<=(const modint &x){return val<<=x.val,*this;}
modint& operator ^=(const modint &x){return val^=x.val,*this;}
modint operator >>(const modint &x){return modint(*this)>>=x;}
modint operator <<(const modint &x){return modint(*this)<<=x;}
modint& operator /=(const modint &x){return *this*=x.inv();}
modint operator +(const modint &x){return modint(*this)+=x;}
modint operator -(const modint &x){return modint(*this)-=x;}
modint operator *(const modint &x){return modint(*this)*=x;}
modint operator /(const modint &x){return modint(*this)/=x;}
modint operator ^(const modint &x){return modint(*this)^=x;}
friend std::ostream& operator<<(std::ostream& os,const modint &a){return os<<a.val;}
friend std::istream& operator>>(std::istream& is,modint &a){return is>>a.val;}
};
typedef modint<998244353>m98;
inline int read(){
int res=0,v=1;
char c=getchar();
while(c<'0'||c>'9'){v=(c=='-'?-1:1);c=getchar();}
while(c>='0'&&c<='9'){res=(res<<3)+(res<<1)+(c^48);c=getchar();}
return res*v;
}
m98 ksm(m98 a,int b){
m98 ans=1;
for(;b;b>>=1){
if(b&1)ans=ans*a;
a=a*a;
}
return ans;
}
m98 a[Max],b[Max];
int rev[Max],limit;
void Fast(m98 *a,int tag){
for(int i=0;i<limit;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int mid=1;mid<limit;mid<<=1){
m98 W=ksm(tag==1?g:invg,(mod-1)/(mid<<1));
for(int R=mid<<1,j=0;j<limit;j+=R){
m98 w=1;
for(int p=0;p<mid;++p,w=w*W){
m98 x=a[p+j],y=w*a[p+j+mid];
a[p+j]=x+y;
a[p+j+mid]=x-y;
}
}
}
if(tag==-1){
m98 inv=m98(limit).inv();
for(int i=0;i<limit;++i)a[i]=a[i]*inv;
}
}
bool Med;
signed main(){
int n=read();
int m=raed();
for(int i=0;i<=n;++i)a[i].val=read();
for(int i=0;i<=m;++i)b[i].val=raed();
limit=1;
int len=0;
while(limit<=n+m)limit<<=1,++len;
for(int i=0;i<=limit;++i)rev[i]=((rev[i>>1]>>1)|((i&1)<<(len-1)));
Fast(a,1);
Fast(b,1);
for(int i=0;i<=limit;++i)a[i]=a[i]*b[i];
Fast(a,-1);
for(int i=0;i<=n+m;++i)cout << a[i] << ' ';
cout << "\n";
cerr<< "Time: "<<clock()/1000.0 << "s\n";
cerr<< "Memory: " << (&Mst-&Med)/1000000.0 << "MB\n";
return 0;
}
/*
*/
多项式牛顿迭代
给定多项式
通过倍增和泰勒展开(不会)得到:
考虑证明:
假设我们已经知道
考虑将
可以注意到
所以有
多项式求逆
设给定的多项式是
于是有:
而当
多项式开根
设给定的多项式是
于是有:
多项式对数函数
假设我们要求
注意到
因为我们只关心余数,所以可以求逆就行。
多项式指数函数
设给定的多项式是
于是有:
多项式快速幂
设给定的多项式是
注意到要求
Code
#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define mk make_pair
#define reaD read
#define raed read
#define haed head
#define cotu cout
#define se second
#define fi first
#define itn int
using namespace std;
bool Mst;
const int Max=6e5+10;
const int mod=998244353;
const int inf=1e9+10;
template <int mod>
struct modint{
int val;
static int norm(const int &x){return x<0?x+mod:x;}
modint inv()const{
int a=val,b=mod,u=1,v=0,t;
while(b>0)t=a/b,swap(a-=t*b,b),swap(u-=t*v,v);
return modint(u);
}
modint():val(0){}
modint(const int &m):val(norm(m)){}
modint(const ll &m):val(norm(m%mod)){}
modint operator -()const{return modint(norm(-val));}
bool operator ==(const modint &x){return val==x.val;}
bool operator !=(const modint &x){return val!=x.val;}
bool operator <=(const modint &x){return val<=x.val;}
bool operator >=(const modint &x){return val>=x.val;}
bool operator >(const modint &x){return val>x.val;}
bool operator <(const modint &x){return val<x.val;}
modint& operator *=(const modint &x){return val=static_cast<int>(1ll*val*x.val%mod),*this;}
modint& operator <<=(const modint &x){return val=(1ll*val<<x.val)%mod,*this;}
modint& operator +=(const modint &x){return val=(1ll*val+x.val)%mod,*this;}
modint& operator -=(const modint &x){return val=norm(1ll*val-x.val),*this;}
modint& operator >>=(const modint &x){return val>>=x.val,*this;}
modint& operator ^=(const modint &x){return val^=x.val,*this;}
modint operator >>(const modint &x){return modint(*this)>>=x;}
modint operator <<(const modint &x){return modint(*this)<<=x;}
modint& operator /=(const modint &x){return *this*=x.inv();}
modint operator +(const modint &x){return modint(*this)+=x;}
modint operator -(const modint &x){return modint(*this)-=x;}
modint operator *(const modint &x){return modint(*this)*=x;}
modint operator /(const modint &x){return modint(*this)/=x;}
modint operator ^(const modint &x){return modint(*this)^=x;}
friend std::ostream& operator<<(std::ostream& os,const modint &a){return os<<a.val;}
friend std::istream& operator>>(std::istream& is,modint &a){return is>>a.val;}
};
typedef modint<998244353>m98;
inline int read(){
int res=0,v=1;
char c=getchar();
while(c<'0'||c>'9'){v=(c=='-'?-1:1);c=getchar();}
while(c>='0'&&c<='9'){res=(res<<3)+(res<<1)+(c^48);c=getchar();}
return res*v;
}
m98 ksm(m98 a,int b){
m98 ans=1;
for(;b;b>>=1){
if(b&1)ans=ans*a;
a=a*a;
}
return ans;
}
m98 inv[Max],frac[Max];
void init(int len) {
frac[0]=inv[0]=1;
for(int i=1;i<=len;++i)frac[i]=frac[i-1]*i;
inv[len]=frac[len].inv();for(int i=len-1;i;--i)inv[i]=inv[i+1]*(i+1);
for(int i=1;i<=len;++i)inv[i]*=frac[i-1];
}
int rev[Max];
void init(int lim,int len){for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));}
struct Poly{ //导和积都到n,否则到n-1;
const int g=3;
const int invg=332748118;
m98 a[Max];
void out(int n,int x=0){for(int i=0;i<n;++i)cout << a[i].val << " ";if(!x)cout << "\n";}
void inte(int n){for(int i=n;i>=0;--i)a[i]=a[i-1]*inv[i];a[0]=0;}
void ReadIn(int n){for(int i=0;i<n;++i)a[i].val=read();}
void der(int n){for(int i=0;i<=n;++i)a[i]=a[i+1]*(i+1);}
m98& operator [](const int x){return a[x];}
void NTT(int tag,int lim){
for(int i=0;i<lim;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int mid=1;mid<lim;mid<<=1){
m98 W=ksm(tag==1?g:invg,(mod-1)/(mid<<1));
for(int R=mid<<1,j=0;j<lim;j+=R){
m98 w=1;
for(int p=0;p<mid;++p,w=w*W){
m98 x=a[j+p],y=a[j+p+mid]*w;
a[j+p]=x+y;
a[j+p+mid]=x-y;
}
}
}
if(tag==-1){
m98 inv=m98(lim).inv();
for(int i=0;i<lim;++i)a[i]*=inv;
}
}
}a;
void GetInv(Poly &y,Poly &x,int n){ //第二个为参数,第一个为返回值
static Poly b[2],c;
int lim=2,len=1,bas=1;
int pos=0;
b[pos][0]=x[0].inv();
while(bas<(n<<1)){
init(lim,len);
pos^=1;
for(int i=0;i<bas;++i)b[pos][i]=b[pos^1][i]<<1;
for(int i=0;i<=lim;++i)c[i]=x[i];
for(int i=lim>>1;i<=lim;++i)c[i]=0;
b[pos^1].NTT(1,lim);c.NTT(1,lim);
for(int i=0;i<=lim;++i)b[pos^1][i]=b[pos^1][i]*b[pos^1][i]*c[i];
b[pos^1].NTT(-1,lim);
for(int i=0;i<bas;++i)b[pos][i]-=b[pos^1][i],b[pos^1][i]=0;
bas<<=1,lim<<=1,++len;
}
for(int i=0;i<n;++i)y[i]=b[pos][i];
for(int i=0;i<bas;++i)b[0][i]=b[1][i]=c[i]=0;
}
void GetSqrt(Poly &y,Poly &x,int n){
static Poly t1,t2,s;
s[0]=1;
int lim=2,len=1,bas=1;
while(bas<(n<<1)){
for(int i=0;i<bas;++i)t1[i]=x[i];
for(int i=0;i<bas;++i)t2[i]=s[i]<<1;
GetInv(t2,t2,bas);init(lim,len);
t2.NTT(1,lim);t1.NTT(1,lim);
for(int i=0;i<=lim;++i)t1[i]=t1[i]*t2[i];
t1.NTT(-1,lim);
for(int i=0;i<bas;++i)s[i]=s[i]/2+t1[i];
bas<<=1,lim<<=1,++len;
}
for(int i=0;i<n;++i)y[i]=s[i];
for(int i=0;i<bas;++i)t1[i]=t2[i]=s[i]=0;
}
void GetLn(Poly &y,Poly&x,int n){
static Poly t1,t2;
for(int i=0;i<n;++i)t1[i]=t2[i]=x[i];
t1.der(n);GetInv(t2,t2,n);
int lim=1,len=0;
while(lim<n<<1)lim<<=1,++len;
init(lim,len);
t1.NTT(1,lim),t2.NTT(1,lim) ;
for(int i=0;i<lim;++i)t1[i]*=t2[i];
t1.NTT(-1,lim);t1.inte(n) ;
for(int i=0;i<n;++i)y[i]=t1[i];
for(int i=0;i<lim;++i)t1[i]=t2[i]=0;
}
void GetExp(Poly &y,Poly &x,int n){
static Poly s,tmp;s[0]=1;
int bas=1,lim=2,len=1;
while(bas<n<<1){
for(int i=0;i<bas;++i)tmp[i]=s[i];
GetLn(tmp,tmp,bas);
for(int i=0;i<bas;++i)tmp[i]=x[i]-tmp[i];
tmp[0]+=1;init(lim,len);tmp.NTT(1,lim),s.NTT(1,lim) ;
for(int i=0;i<lim;++i)s[i]=s[i]*tmp[i];
s.NTT(-1,lim) ;
for(int i=bas;i<lim;++i)s[i]=0;
lim<<=1,bas<<=1,++len;
}
for(int i=0;i<n;++i)y[i]=s[i];
for(itn i=0;i<bas;++i)s[i]=tmp[i]=0;
}
int Si=0;
void expow(Poly &y,Poly &x,int n,m98 k,int k1){
static Poly s;
int z=n+1;
for(int i=0;i<n;++i)if(x[i]!=0){z=i;break;}
if(z==n+1||(z!=0&&Si)){
for(int i=0;i<n;++i)y[i]=0;
return;
}
for(itn i=z;i<n;++i)s[i-z]=x[i]/x[z];GetLn(s,s,n-z);
for(int i=0;i<n-z;++i)s[i]*=k;GetExp(s,s,n-z);
for(int i=0;i<n-z;++i)s[i]=s[i]*ksm(x[z],k1);
for(int i=0;i<n;++i)y[i]=0;
for(int i=0;1ll*z*k.val+i<n;++i)y[i+z*k.val]=s[i];
for(int i=0;i<n;++i)s[i]=0;
}
Poly b;
m98 k;
ll k1;
void In(){
k=k1=0;
char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9'){Si=Si|(k*10+(c^48)<k),k=(k<<3)+(k<<1)+(c^48),k1=(k1*10%(mod-1)+(c^48))%(mod-1),c=getchar();}
}
bool Med;
signed main(){
init(300000);
cerr<< "Time: "<<clock()/1000.0 << "s\n";
cerr<< "Memory: " << (&Mst-&Med)/1000000.0 << "MB\n";
return 0;
}
第二类斯特林数
定义
第二类斯特林数
递推公式
通过组合意义来解释:
当加入一个球时可以将其单独放入一个空盒子中,方案数为
也可以将其放入一个非空的盒子中,方案数为
通过加法原理得到递推公式。
通向公式
可以使用容斥证明该式子:
- 将
n 个区分的球放入m 个区分的盒子中且允许有空盒子的方案为A_m , - 将
n 个区分的球放入m 个区分的盒子但不能有空盒子的方案是B_m 。
那么有 :
通过二项式反演可得:
因为
第二类斯特林数·行
发现其通向公式
这是一个卷积的形式,所以可以 NTT 求。
#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define mk make_pair
#define reaD read
#define raed read
#define haed head
#define cotu cout
#define se second
#define fi first
#define itn int
using namespace std;
bool Mst;
const int Max=1e6+10;
const int mod=167772161;
const int inf=1e9+10;
template <int mod>
struct modint{
int val;
static int norm(const int &x){return x<0?x+mod:x;}
modint inv()const{
int a=val,b=mod,u=1,v=0,t;
while(b>0)t=a/b,swap(a-=t*b,b),swap(u-=t*v,v);
return modint(u);
}
modint():val(0){}
modint(const int &m):val(norm(m)){}
modint(const ll &m):val(norm(m%mod)){}
modint operator -()const{return modint(norm(-val));}
bool operator ==(const modint &x){return val==x.val;}
bool operator !=(const modint &x){return val!=x.val;}
bool operator <=(const modint &x){return val<=x.val;}
bool operator >=(const modint &x){return val>=x.val;}
bool operator >(const modint &x){return val>x.val;}
bool operator <(const modint &x){return val<x.val;}
modint& operator *=(const modint &x){return val=static_cast<int>(1ll*val*x.val%mod),*this;}
modint& operator <<=(const modint &x){return val=(1ll*val<<x.val)%mod,*this;}
modint& operator +=(const modint &x){return val=(1ll*val+x.val)%mod,*this;}
modint& operator -=(const modint &x){return val=norm(1ll*val-x.val),*this;}
modint& operator >>=(const modint &x){return val>>=x.val,*this;}
modint& operator ^=(const modint &x){return val^=x.val,*this;}
modint operator >>(const modint &x){return modint(*this)>>=x;}
modint operator <<(const modint &x){return modint(*this)<<=x;}
modint& operator /=(const modint &x){return *this*=x.inv();}
modint operator +(const modint &x){return modint(*this)+=x;}
modint operator -(const modint &x){return modint(*this)-=x;}
modint operator *(const modint &x){return modint(*this)*=x;}
modint operator /(const modint &x){return modint(*this)/=x;}
modint operator ^(const modint &x){return modint(*this)^=x;}
friend std::ostream& operator<<(std::ostream& os,const modint &a){return os<<a.val;}
friend std::istream& operator>>(std::istream& is,modint &a){return is>>a.val;}
};
typedef modint<167772161>m16;
inline int read(){
int res=0,v=1;
char c=getchar();
while(c<'0'||c>'9'){v=(c=='-'?-1:1);c=getchar();}
while(c>='0'&&c<='9'){res=(res<<3)+(res<<1)+(c^48);c=getchar();}
return res*v;
}
int rev[Max];
void init(int lim,int len){
for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
}
m16 ksm(m16 a,int b){
m16 ans=1;
for(;b;b>>=1){
if(b&1)ans=ans*a;
a=a*a;
}
return ans;
}
struct Poly{
const m16 g=3;
const m16 invg=(mod+1)/3;
m16 a[Max];
m16& operator [](const int x){return a[x];}
void NTT(int tag,int lim){
for(int i=0;i<lim;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int mid=1;mid<lim;mid<<=1){
m16 W=ksm(tag==1?g:invg,(mod-1)/(mid<<1));
for(int R=mid<<1,j=0;j<lim;j+=R){
m16 w=1;
for(int p=0;p<mid;++p,w=w*W){
m16 x=a[p+j],y=a[p+j+mid]*w;
a[p+j]=x+y;
a[p+j+mid]=x-y;
}
}
}
if(tag==-1){
m16 inv=m16(lim).inv();
for(int i=0;i<lim;++i)a[i]*=inv;
}
}
};
Poly a,b;
m16 frac[Max],inv[Max];
bool Med;
signed main(){
int n=read();
inv[0]=frac[0]=1;
for(int i=1;i<=n;++i)frac[i]=frac[i-1]*i;
inv[n]=frac[n].inv();
for(int i=n-1;i>=1;--i)inv[i]=inv[i+1]*(i+1);
for(int i=0;i<=n;++i){
a[i]=ksm(i,n)*inv[i];
b[i]=inv[i]*(i%2==0?1:-1);
}
int lim=1,len=0;
while(lim<=n+n)lim<<=1,++len;
init(lim,len);
a.NTT(1,lim),b.NTT(1,lim) ;
for(int i=0;i<lim;++i)a[i]*=b[i];
a.NTT(-1,lim) ;
for(int i=0;i<=n;++i)cotu << a[i] << ' ';
cotu << "\n";
cerr<< "Time: "<<clock()/1000.0 << "s\n";
cerr<< "Memory: " << (&Mst-&Med)/1000000.0 << "MB\n";
return 0;
}
第二类斯特林数·列
先假设每个盒子也要区分,考虑对于一个盒子计算,其生成函数是
#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define mk make_pair
#define reaD read
#define raed read
#define haed head
#define cotu cout
#define se second
#define fi first
#define itn int
using namespace std;
bool Mst;
const int Max=2e6+10;
const int mod=167772161;
const int inf=1e9+10;
template <int mod>
struct modint{
int val;
static int norm(const int &x){return x<0?x+mod:x;}
modint inv()const{
int a=val,b=mod,u=1,v=0,t;
while(b>0)t=a/b,swap(a-=t*b,b),swap(u-=t*v,v);
return modint(u);
}
modint():val(0){}
modint(const int &m):val(norm(m)){}
modint(const ll &m):val(norm(m%mod)){}
modint operator -()const{return modint(norm(-val));}
bool operator ==(const modint &x){return val==x.val;}
bool operator !=(const modint &x){return val!=x.val;}
bool operator <=(const modint &x){return val<=x.val;}
bool operator >=(const modint &x){return val>=x.val;}
bool operator >(const modint &x){return val>x.val;}
bool operator <(const modint &x){return val<x.val;}
modint& operator *=(const modint &x){return val=static_cast<int>(1ll*val*x.val%mod),*this;}
modint& operator <<=(const modint &x){return val=(1ll*val<<x.val)%mod,*this;}
modint& operator +=(const modint &x){return val=(1ll*val+x.val)%mod,*this;}
modint& operator -=(const modint &x){return val=norm(1ll*val-x.val),*this;}
modint& operator >>=(const modint &x){return val>>=x.val,*this;}
modint& operator ^=(const modint &x){return val^=x.val,*this;}
modint operator >>(const modint &x){return modint(*this)>>=x;}
modint operator <<(const modint &x){return modint(*this)<<=x;}
modint& operator /=(const modint &x){return *this*=x.inv();}
modint operator +(const modint &x){return modint(*this)+=x;}
modint operator -(const modint &x){return modint(*this)-=x;}
modint operator *(const modint &x){return modint(*this)*=x;}
modint operator /(const modint &x){return modint(*this)/=x;}
modint operator ^(const modint &x){return modint(*this)^=x;}
friend std::ostream& operator<<(std::ostream& os,const modint &a){return os<<a.val;}
friend std::istream& operator>>(std::istream& is,modint &a){return is>>a.val;}
};
typedef modint<167772161>m16;
inline int read(){
int res=0,v=1;
char c=getchar();
while(c<'0'||c>'9'){v=(c=='-'?-1:1);c=getchar();}
while(c>='0'&&c<='9'){res=(res<<3)+(res<<1)+(c^48);c=getchar();}
return res*v;
}
m16 ksm(m16 a,int b){
m16 ans=1;
for(;b;b>>=1){
if(b&1)ans=ans*a;
a=a*a;
}
return ans;
}
m16 inv[Max],frac[Max],Inv[Max];
void init(int len) {
frac[0]=inv[0]=1;
for(int i=1;i<=len;++i)frac[i]=frac[i-1]*i;
Inv[len]=frac[len].inv();for(int i=len-1;i;--i)Inv[i]=Inv[i+1]*(i+1);
for(int i=1;i<=len;++i)inv[i]=Inv[i]*frac[i-1];
}
int rev[Max];
void init(int lim,int len){for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));}
struct Poly{ //导和积都到n,否则到n-1;
const m16 g=3;
const m16 invg=(mod+1)/3;
m16 a[Max];
void out(int n,int x=0){for(int i=0;i<n;++i)cout << a[i].val << " ";if(!x)cout << "\n";}
void inte(int n){for(int i=n;i>=0;--i)a[i]=a[i-1]*inv[i];a[0]=0;}
void ReadIn(int n){for(int i=0;i<n;++i)a[i].val=read();}
void der(int n){for(int i=0;i<=n;++i)a[i]=a[i+1]*(i+1);}
m16& operator [](const int x){return a[x];}
void NTT(int tag,int lim){
for(int i=0;i<lim;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int mid=1;mid<lim;mid<<=1){
m16 W=ksm(tag==1?g:invg,(mod-1)/(mid<<1));
for(int R=mid<<1,j=0;j<lim;j+=R){
m16 w=1;
for(int p=0;p<mid;++p,w=w*W){
m16 x=a[j+p],y=a[j+p+mid]*w;
a[j+p]=x+y;
a[j+p+mid]=x-y;
}
}
}
if(tag==-1){
m16 inv=m16(lim).inv();
for(int i=0;i<lim;++i)a[i]*=inv;
}
}
}a;
void GetInv(Poly &y,Poly &x,int n){ //第二个为参数,第一个为返回值
static Poly b[2],c;
int lim=2,len=1,bas=1;
int pos=0;
b[pos][0]=x[0].inv();
while(bas<=(n<<1)){
init(lim,len);
pos^=1;
for(int i=0;i<bas;++i)b[pos][i]=b[pos^1][i]<<1;
for(int i=0;i<=lim;++i)c[i]=x[i];
for(int i=lim>>1;i<=lim;++i)c[i]=0;
b[pos^1].NTT(1,lim);c.NTT(1,lim);
for(int i=0;i<=lim;++i)b[pos^1][i]=b[pos^1][i]*b[pos^1][i]*c[i];
b[pos^1].NTT(-1,lim);
for(int i=0;i<bas;++i)b[pos][i]-=b[pos^1][i],b[pos^1][i]=0;
bas<<=1,lim<<=1,++len;
}
for(int i=0;i<n;++i)y[i]=b[pos][i];
for(int i=0;i<bas;++i)b[0][i]=b[1][i]=c[i]=0;
}
void GetLn(Poly &y,Poly&x,int n){
static Poly t1,t2;
for(int i=0;i<n;++i)t1[i]=t2[i]=x[i];
t1.der(n);GetInv(t2,t2,n);
int lim=1,len=0;
while(lim<=n<<1)lim<<=1,++len;
init(lim,len);
t1.NTT(1,lim),t2.NTT(1,lim) ;
for(int i=0;i<lim;++i)t1[i]*=t2[i];
t1.NTT(-1,lim);t1.inte(n) ;
for(int i=0;i<n;++i)y[i]=t1[i];
for(int i=0;i<lim;++i)t1[i]=t2[i]=0;
}
void GetExp(Poly &y,Poly &x,int n){
static Poly s,tmp;s[0]=1;
int bas=1,lim=2,len=1;
while(bas<=n<<1){
for(int i=0;i<bas;++i)tmp[i]=s[i];
GetLn(tmp,tmp,bas);
for(int i=0;i<bas;++i)tmp[i]=x[i]-tmp[i];
tmp[0]+=1;init(lim,len);tmp.NTT(1,lim),s.NTT(1,lim) ;
for(int i=0;i<lim;++i)s[i]=s[i]*tmp[i];
s.NTT(-1,lim) ;
for(int i=bas;i<lim;++i)s[i]=0;
lim<<=1,bas<<=1,++len;
}
for(int i=0;i<n;++i)y[i]=s[i];
for(itn i=0;i<bas;++i)s[i]=tmp[i]=0;
}
void expow(Poly &y,Poly &x,int n,int k){
static Poly s;
for(itn i=0;i<=n;++i)s[i]=x[i];GetLn(s,s,n);
for(int i=0;i<=n;++i)s[i]*=k;GetExp(s,s,n);
for(int i=0;i<=n;++i)y[i]=s[i];
for(int i=0;i<=n;++i)s[i]=0;
}
Poly b;
bool Med;
signed main(){
int n=read()+1;
int k=read();
init(n<<3);
for(int i=1;i<n;++i)b[i-1]=Inv[i];
expow(b,b,n,k);
for(int i=0;i<n;++i){
if(i<k)cout << 0 << " ";
else cout << (b[i-k]*Inv[k]*frac[i])<< ' ';
}
cerr<< "Time: "<<clock()/1000.0 << "s\n";
cerr<< "Memory: " << (&Mst-&Med)/1000000.0 << "MB\n";
return 0;
}
/*
第一类斯特林数
定义
第一类斯特林数
递推公式
因为加入一个元素时有以下两种情况:
- 加在之前一个轮换中,此时它可以加在
n-1 个元素的右边,故有(n-1)\begin{bmatrix}n-1\\m\end{bmatrix} 种方案 - 新开一个轮换,故有
\begin{bmatrix}n-1\\m-1\end{bmatrix} 种方案
第一类斯特林数·行
考虑像第二类斯特林数
第一类斯特林数·列
可考虑对于每个轮换生成函数为
#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define mk make_pair
#define reaD read
#define raed read
#define haed head
#define cotu cout
#define se second
#define fi first
#define itn int
using namespace std;
bool Mst;
const int Max=2e6+10;
const int mod=167772161;
const int inf=1e9+10;
template <int mod>
struct modint{
int val;
static int norm(const int &x){return x<0?x+mod:x;}
modint inv()const{
int a=val,b=mod,u=1,v=0,t;
while(b>0)t=a/b,swap(a-=t*b,b),swap(u-=t*v,v);
return modint(u);
}
modint():val(0){}
modint(const int &m):val(norm(m)){}
modint(const ll &m):val(norm(m%mod)){}
modint operator -()const{return modint(norm(-val));}
bool operator ==(const modint &x){return val==x.val;}
bool operator !=(const modint &x){return val!=x.val;}
bool operator <=(const modint &x){return val<=x.val;}
bool operator >=(const modint &x){return val>=x.val;}
bool operator >(const modint &x){return val>x.val;}
bool operator <(const modint &x){return val<x.val;}
modint& operator *=(const modint &x){return val=static_cast<int>(1ll*val*x.val%mod),*this;}
modint& operator <<=(const modint &x){return val=(1ll*val<<x.val)%mod,*this;}
modint& operator +=(const modint &x){return val=(1ll*val+x.val)%mod,*this;}
modint& operator -=(const modint &x){return val=norm(1ll*val-x.val),*this;}
modint& operator >>=(const modint &x){return val>>=x.val,*this;}
modint& operator ^=(const modint &x){return val^=x.val,*this;}
modint operator >>(const modint &x){return modint(*this)>>=x;}
modint operator <<(const modint &x){return modint(*this)<<=x;}
modint& operator /=(const modint &x){return *this*=x.inv();}
modint operator +(const modint &x){return modint(*this)+=x;}
modint operator -(const modint &x){return modint(*this)-=x;}
modint operator *(const modint &x){return modint(*this)*=x;}
modint operator /(const modint &x){return modint(*this)/=x;}
modint operator ^(const modint &x){return modint(*this)^=x;}
friend std::ostream& operator<<(std::ostream& os,const modint &a){return os<<a.val;}
friend std::istream& operator>>(std::istream& is,modint &a){return is>>a.val;}
};
typedef modint<167772161>m16;
inline int read(){
int res=0,v=1;
char c=getchar();
while(c<'0'||c>'9'){v=(c=='-'?-1:1);c=getchar();}
while(c>='0'&&c<='9'){res=(res<<3)+(res<<1)+(c^48);c=getchar();}
return res*v;
}
m16 ksm(m16 a,int b){
m16 ans=1;
for(;b;b>>=1){
if(b&1)ans=ans*a;
a=a*a;
}
return ans;
}
m16 inv[Max],frac[Max],Inv[Max];
void init(int len) {
frac[0]=inv[0]=1;
for(int i=1;i<=len;++i)frac[i]=frac[i-1]*i;
Inv[len]=frac[len].inv();for(int i=len-1;i;--i)Inv[i]=Inv[i+1]*(i+1);
for(int i=1;i<=len;++i)inv[i]=Inv[i]*frac[i-1];
}
int rev[Max];
void init(int lim,int len){for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));}
struct Poly{ //导和积都到n,否则到n-1;
const m16 g=3;
const m16 invg=(mod+1)/3;
m16 a[Max];
void out(int n,int x=0){for(int i=0;i<n;++i)cout << a[i].val << " ";if(!x)cout << "\n";}
void inte(int n){for(int i=n;i>=0;--i)a[i]=a[i-1]*inv[i];a[0]=0;}
void ReadIn(int n){for(int i=0;i<n;++i)a[i].val=read();}
void der(int n){for(int i=0;i<=n;++i)a[i]=a[i+1]*(i+1);a[n]=0;}
m16& operator [](const int x){return a[x];}
void NTT(int tag,int lim){
for(int i=0;i<lim;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int mid=1;mid<lim;mid<<=1){
m16 W=ksm(tag==1?g:invg,(mod-1)/(mid<<1));
for(int R=mid<<1,j=0;j<lim;j+=R){
m16 w=1;
for(int p=0;p<mid;++p,w=w*W){
m16 x=a[j+p],y=a[j+p+mid]*w;
a[j+p]=x+y;
a[j+p+mid]=x-y;
}
}
}
if(tag==-1){
m16 inv=m16(lim).inv();
for(int i=0;i<lim;++i)a[i]*=inv;
}
}
}a;
void GetInv(Poly &y,Poly &x,int n){ //第二个为参数,第一个为返回值
static Poly b[2],c;
int lim=2,len=1,bas=1;
int pos=0;
b[pos][0]=x[0].inv();
while(bas<(n<<1)){
init(lim,len);
pos^=1;
for(int i=0;i<bas;++i)b[pos][i]=b[pos^1][i]<<1;
for(int i=0;i<=lim;++i)c[i]=x[i];
for(int i=lim>>1;i<=lim;++i)c[i]=0;
b[pos^1].NTT(1,lim);c.NTT(1,lim);
for(int i=0;i<=lim;++i)b[pos^1][i]=b[pos^1][i]*b[pos^1][i]*c[i];
b[pos^1].NTT(-1,lim);
for(int i=0;i<bas;++i)b[pos][i]-=b[pos^1][i],b[pos^1][i]=0;
bas<<=1,lim<<=1,++len;
}
for(int i=0;i<n;++i)y[i]=b[pos][i];
for(int i=0;i<bas;++i)b[0][i]=b[1][i]=c[i]=0;
}
void GetLn(Poly &y,Poly&x,int n){
static Poly t1,t2;
for(int i=0;i<n;++i)t1[i]=t2[i]=x[i];
t1.der(n);GetInv(t2,t2,n);
int lim=1,len=0;
while(lim<n<<1)lim<<=1,++len;
init(lim,len);
t1.NTT(1,lim),t2.NTT(1,lim) ;
for(int i=0;i<lim;++i)t1[i]*=t2[i];
t1.NTT(-1,lim);t1.inte(n) ;
for(int i=0;i<n;++i)y[i]=t1[i];
for(int i=0;i<lim;++i)t1[i]=t2[i]=0;
}
void GetExp(Poly &y,Poly &x,int n){
static Poly s,tmp;s[0]=1;
int bas=1,lim=2,len=1;
while(bas<n<<1){
for(int i=0;i<bas;++i)tmp[i]=s[i];
GetLn(tmp,tmp,bas);
for(int i=0;i<bas;++i)tmp[i]=x[i]-tmp[i];
tmp[0]+=1;init(lim,len);tmp.NTT(1,lim),s.NTT(1,lim) ;
for(int i=0;i<lim;++i)s[i]=s[i]*tmp[i];
s.NTT(-1,lim) ;
for(int i=bas;i<lim;++i)s[i]=0;
lim<<=1,bas<<=1,++len;
}
for(int i=0;i<n;++i)y[i]=s[i];
for(itn i=0;i<bas;++i)s[i]=tmp[i]=0;
}
void expow(Poly &y,Poly &x,int n,int k){
static Poly s;
for(itn i=0;i<n;++i)s[i]=x[i];GetLn(s,s,n);
for(int i=0;i<n;++i)s[i]*=k;GetExp(s,s,n);
for(int i=0;i<n;++i)y[i]=s[i];
for(int i=0;i<n;++i)s[i]=0;
}
Poly b;
bool Med;
signed main(){
int n=read()+1;
int k=read();
init(n);
for(int i=0;i<n-1;++i)b[i]=inv[i+1];
expow(b,b,n,k);
m16 z=frac[k].inv();
for(int i=0;i<n;++i){
if(i<k)cout << 0 << " ";
else cout << (b[i-k]*z*frac[i]).val<< ' ';
}
cerr<< "Time: "<<clock()/1000.0 << "s\n";
cerr<< "Memory: " << (&Mst-&Med)/1000000.0 << "MB\n";
return 0;
}
/*
*/
生成函数
定义
生成函数又称母函数,是一种形式幂级数,即只关心未知数幂次和常数,不关心未知数取值。
一般的生成函数形式为
其中
对于两个生成函数
性质
求导
我们定义
对于一般的求导式子依然成立:
-
[F(x)+G(x)]'=F(x)'+G(x)' -
[F(x)G(x)]'=F'(x)G(x)+G'(x)F(x) -
[F(G(x))]'=F'(G(x))G'(x)
Maclaurin 公式
麦克劳林展开式泰勒展开在函数
我们设
分类
常见生成函数
形如
当序列
我们可以找到其逆元为
常见的普通生成函数有
Fibonacci
其递归式定义为
我们令
将其作差我们可以发现
设:
Catalan
定义卡特兰数序列
同理,我们令
令
我们可以发现,式子括号中的部分类似卷积形式,那么我们可以将其转换为:
解出这个一元二次方程(
对其分子有理化:
我们可以发现,当
后面就是广义二项式定理(不会),只知道最终结果为: