囧仙
2022-06-29 12:04:24
洛谷早期日报有一篇就是在讲小球与盒子的,但是有的地方讲的不是很清楚,而且一些情形的多项式做法没讲,所以我来填坑()
小球盒子模型,大致上是将
本文中,小球盒子的分类一共有
做一件事情,完成它有
n 类方式,第一类方式有m_1 种方法,第二类方式有m_2 种方法,……,第n 类方式有m_n 种方法,那么完成这件事情共有m_1+m_2+\cdots m_n=\sum_{i=1}^n m_i 种方法。
做一件事,完成它需要分成
n 个步骤,做第一 步有m_1 种不同的方法,做第二步有m_2 种不同的方法,……,做第n 步有m_n 种不同的方法。那么完成这件事共有m_1\cdot m_2\cdots m_n=\prod_{i=1}^n m_i 种不同的方法。
由此可以求出排列数
为了简便起见,
因此,
由此引入组合数
丢个图。这个是在
组合数的推导也比较简单:它选出来的
为了美观,组合数还有另外一种写法:
剩下来的知识就在十二重计数法里面体现啦。
考虑从每个小球出发。每个球都有
如图所示,是
使用快速幂,时间复杂度为
因为盒子最多只能装
时间复杂度为
这里开始要引入二项式反演。
容易发现,
但是
首先给出二项式反演的式子:
在证明之前,我们会频繁用到二项式定理。即:
二项式定理的证明并不太难。考虑右式由
例如,
考虑二项式反演的证明:
现在已知:
于是得到,
直接暴力枚举+快速幂,时间复杂度为
这里开始要引入第二类斯特林数。
第二类斯特林数
回到
下面问题在于怎么求出
考虑多项式乘法。对于两个多项式
那么只要构造如下多项式:
两者相乘,得到:
因此总的时间复杂度为
轻松题。显然如果
时间复杂度为
轻松题。显然是第二类斯特林数的定义。直接拿第二类斯特林数行的做法求出一整行,再取出其中第
时间复杂度为
考虑使用隔板法。
将
从左往右将每堆球丢在一个盒子里,那么我们就做到了「将
但是现在盒子可以为空。做法也很简单:先往每个盒子里塞一个球,最后把这个球拿走就行了。换言之,先求「将
时间复杂度为
轻松题。因为每个球只能放到一个盒子里,每个盒子最多装一个球,并且每个球必须要放到一个盒子里,因此考虑
时间复杂度为
就是插板法的原版,上文已经提及。答案为
时间复杂度为
它 来 了。
记
如图所示,是
容易得到状态转移方程:
下面考虑如何加快
我们记
考虑到
两边取对数,得到:
考虑一个经典结论:
证明如下:
(也就是先求导再积分)
因为我们只需要
算出
轻松题,与
时间复杂度为
和
时间复杂度为
#include<bits/stdc++.h>
#define up(l,r,i) for(int i=l,END##i=r;i<=END##i;++i)
#define dn(r,l,i) for(int i=r,END##i=l;i>=END##i;--i)
using namespace std;
typedef long long LL;
typedef unsigned int u32;
typedef unsigned long long u64;
const int INF =2147483647;
int qread(){
int w=1,c,r=0;
while((c=getchar())> '9'||c< '0') w=(c=='-'?-1:1); r=c-'0';
while((c=getchar())>='0'&&c<='9') r=r*10+c-'0';
return r*w;
}
const int MAX_ =(1<<20)+3;
struct cplx{
double a,b; cplx(double _a=0,double _b=0):a(_a),b(_b){}
cplx operator +(cplx t){return cplx(a+t.a,b+t.b);}
cplx operator -(cplx t){return cplx(a-t.a,b-t.b);}
cplx operator *(cplx t){return cplx(a*t.a-b*t.b,a*t.b+b*t.a);}
cplx operator *(int t){return cplx(a*t,b*t);}
};
const int MOD =998244353;
struct modint{
unsigned w;
modint(u32 _w=0){w=_w;}
modint pwr(modint x,modint y){
modint r=1; while(y.w){if(y.w&1) r=r*x; y.w>>=1,x=x*x;}
return r;
}
modint pwr(modint y){
modint x(w),r=1; while(y.w){if(y.w&1) r=r*x; y.w>>=1,x=x*x;}
return r;
}
modint inv(){return modint(w).pwr(MOD-2);}
modint dec(){if(w>=MOD) return w-MOD; return w;}
modint operator +(modint t){return modint(w+t.w ).dec();}
modint operator -(modint t){return modint(w-t.w+MOD).dec();}
modint operator *(modint t){return modint(1ull*w*t.w%MOD);}
modint operator /(modint t){
if(w%t.w==0) return modint(w/t.w); return modint(w)*t.inv();
}
modint operator -(){return w?MOD-w:0;}
int to_int(){return w;}
};
struct Minv{
modint inv(modint x){return x.inv();}
void inv(int n,modint *T){
T[1]=1; up(2,n,i) T[i]=modint(MOD-MOD/i)*T[MOD%i];
}
void inv(int n,modint *A,modint *B){
static modint P[MAX_];
static modint Q[MAX_];
P[ 0]= A[ 0] ; up(1,n-1,i) P[i]=P[i-1]*A[i ];
Q[n-1]=inv(P[n-1]); dn(n-2,0,i) Q[i]=Q[i+1]*A[i+1];
up(1,n-1,i) B[i]=Q[i]*P[i-1]; B[0]=Q[0];
}
void fac(int n,modint *A){
A[0]=1; up(1,n,i) A[i]=A[i-1]*modint(i);
}
}minv;
const long double pi=acos(-1);
class Poly{
public:
void FFT(int n,cplx *Z){
static int W[MAX_];
int l=1; W[0]=0; while(n>>=1)
up(0,l-1,i) W[l++]=W[i]<<1|1,W[i]<<=1;
up(0,l-1,i) if(W[i]>i) swap(Z[i],Z[W[i]]);
for(n=l>>1,l=1;n;n>>=1,l<<=1){
cplx *S=Z,o(cos(pi/l),sin(pi/l)); up(0,n-1,i){
cplx s(1,0); up(0,l-1,j){
cplx x=S[j]+s*S[j+l],y=S[j]-s*S[j+l];
S[j]=x,S[j+l]=y,s=s*o;
}
S+=l<<1;
}
}
}
void IFFT(int n,cplx *Z){
FFT(n,Z); reverse(Z+1,Z+n);
up(0,n-1,i) Z[i].a/=1.0*n,Z[i].b/=1.0*n;
}
void NTT(int n,modint *Z){
static int W[MAX_];
modint g(3); int l=1; W[0]=0; while(n>>=1)
up(0,l-1,i) W[l++]=W[i]<<1|1,W[i]<<=1;
up(0,l-1,i) if(W[i]>i) swap(Z[i],Z[W[i]]);
for(n=l>>1,l=1;n;n>>=1,l<<=1){
modint *S=Z,o=g.pwr((MOD-1)/l/2); up(0,n-1,i){
modint s(1); up(0,l-1,j){
modint x=S[j]+s*S[j+l],y=S[j]-s*S[j+l];
S[j]=x,S[j+l]=y,s=s*o;
}
S+=l<<1;
}
}
}
void INTT(int n,modint *Z){
NTT(n,Z); reverse(Z+1,Z+n); modint o(n); o=o.pwr(MOD-2);
up(0,n-1,i) Z[i]=Z[i]*o;
}
void MUL(int n,modint *A,modint *B){
NTT(n,A),NTT(n,B); up(0,n-1,i) A[i]=A[i]*B[i]; INTT(n,A);
}
void INV(int n,modint *Z,modint *T){
static modint A[MAX_];
up(0,n-1,i) T[i]=0; T[0]=Z[0].pwr(MOD-2).to_int();
for(int l=1;l<n;l<<=1){
up(0,2*l-1,i) A[i]=Z[i]; up(2*l,4*l-1,i) A[i]=0;
NTT(4*l,A),NTT(4*l,T);
up(0,4*l-1,i) T[i]=modint(2)*T[i]-A[i]*T[i]*T[i];
INTT(4*l,T);
up(2*l,4*l-1,i) T[i]=0;
}
}
void DIF(int n,modint *Z,modint *T){
up(0,n-2,i) T[i]=Z[i+1]*modint(i+1); T[n-1]=0;
}
void INT(int n,int c,modint *Z,modint *T){
up(1,n-1,i) T[i]=Z[i-1]*modint(i).pwr(MOD-2);
T[0]=modint(c);
}
void LN(int n,modint *Z,modint *T){
static modint A[MAX_];
static modint B[MAX_];
up(0,2*n-1,i) A[i]=B[i]=0;
DIF(n,Z,A),INV(n,Z,B),MUL(2*n,A,B),INT(n,0,A,T);
}
void EXP(int n,modint *Z,modint *T){
static modint A[MAX_];
static modint B[MAX_];
up(1,2*n-1,i) T[i]=0; T[0]=1;
for(int l=1;l<n;l<<=1){
LN(2*l,T,A);
up(0,2*l-1,i) B[i]=-A[i]+Z[i]; B[0]=B[0]+modint(1);
up(2*l,4*l-1,i) T[i]=B[i]=0;
MUL(4*l,T,B);
}
}
void SQT(int n,modint *Z,modint *T){
static modint A[MAX_];
static modint B[MAX_];
up(1,2*n-1,i) T[i]=0; T[0]=1; modint o=modint(2).inv();
for(int l=1;l<n;l<<=1){
INV(2*l,T,A);
up( 0,2*l-1,i) B[i]=Z[i];
up(2*l,4*l-1,i) A[i]=B[i]=0;
MUL(4*l,A,B);
up( 0,2*l-1,i) T[i]=(T[i]+A[i])*o;
}
}
void SHF(int n,modint c,modint *Z,modint *T){
static modint A[MAX_];
static modint B[MAX_];
static modint F[MAX_];
static modint G[MAX_];
modint o(1);
minv.fac(n-1,F),minv.inv(n,F,G);
up(0,n-1,i) A[i]=Z[n-1-i]*F[n-1-i];
up(0,n-1,i) B[i]=G[i]*o,o=o*c;
int l=1; while(l<2*n-1) l<<=1;
up(n,l-1,i) A[i]=B[i]=0; MUL(l,A,B);
up(0,n-1,i) T[n-1-i]=G[n-1-i]*A[i];
}
void S2L(int n,modint *T){
static modint F[MAX_];
static modint G[MAX_];
static modint H[MAX_];
static int P[MAX_]; int p=0,l=1;
up(1,n,i) H[i]=modint(0); H[1]=1;
up(2,n,i){
if(H[i].w==0) P[++p]=i,H[i]=modint(i).pwr(n);
for(int j=1;j<=p&&P[j]<=n/i;++j){
H[i*P[j]]=H[i]*H[P[j]]; if(i%P[j]==0) break;
}
}
static modint U[MAX_];
minv.fac(n,F),minv.inv(n+1,F,G);
up(0,n,i) T[i]=G[i]*H[i];
up(0,n,i) U[i]=G[i]*modint(i&1?(MOD-1):1);
while(l<2*n+1) l<<=1;
MUL(l,T,U);
}
void DEP(int n,modint *T){ //下降幂
if(n==1){ T[0]=0,T[1]=1; return; }
int u=n>>1,l=1; while(l<2*u+1) l<<=1;
modint *A=new modint[l];
DEP(u,T), SHF(u+1,modint(MOD-u),T,A);
up(u+1,l-1,i) T[i]=A[i]=0; MUL(l,T,A);
if(n%2==1){ //*= x-n+1
dn(n,1,i) T[i]=T[i]*modint(MOD+1-n)+T[i-1];
T[0]=T[0]*modint(MOD+1-n);
}
}
void ASP(int n,modint *T){
if(n==1){ T[0]=0,T[1]=1; return; }
int u=n>>1,l=1; while(l<2*u+1) l<<=1;
modint *A=new modint[l];
ASP(u,T), SHF(u+1,u,T,A);
up(u+1,l-1,i) T[i]=A[i]=0; MUL(l,T,A);
if(n%2==1){ //*= x+n-1
dn(n,1,i) T[i]=T[i]*modint(n-1)+T[i-1];
T[0]=T[0]*modint(n-1);
}
}
void S2C(int n,int m,modint *T){
static modint H[MAX_]; int l=1; while(l<n+1) l<<=1;
up(0,l-1,i) H[i]=0; DEP(m+1,H);
up(0,m,i) H[i]=H[i+1]; up(m+1,l-1,i) H[i]=0;
reverse(H,H+m+1); INV(l,H,T);
}
void FDT(int n,modint *T){
static modint E[MAX_];
static modint F[MAX_];
minv.fac(n-1,F),minv.inv(n,F,E);
up(n,2*n-1,i) E[i]=T[i]=0;
MUL(2*n,T,E); up(n,2*n-1,i) T[i]=0;
}
void IFDT(int n,modint *T){
static modint E[MAX_];
static modint F[MAX_];
minv.fac(n-1,F),minv.inv(n,F,E);
up(0,n-1,i) if(i&1) E[i]=-E[i];
up(n,2*n-1,i) E[i]=T[i]=0;
MUL(2*n,T,E); up(n,2*n-1,i) T[i]=0;
}
void PML(int n,modint *A,modint *B){
static modint F[MAX_]; minv.fac(n-1,F);
FDT(n,A),FDT(n,B); up(0,n-1,i) A[i]=A[i]*B[i]*F[i];
IFDT(n,A);
}
void S1L(int n,modint *A){
ASP(n,A);
}
}poly;
const int MAXN=(1<<20)+3;
modint M[MAXN],N[MAXN],F[MAXN],G[MAXN],O[MAXN],P[MAXN],Q[MAXN];
int main(){
int n=qread(),m=qread(),t=n+m; modint ans=0;
poly.S2L(n,N),poly.S2L(m,M);
minv.fac(t,F),minv.inv(t+1,F,G),minv.inv(t,O);
ans=modint(m).pwr(n);
printf("%d\n",ans.to_int()),ans=0; // I
ans=(m>=n?F[m]*G[m-n]:0);
printf("%d\n",ans.to_int()),ans=0; // II
ans=(n>=m?N[m]*F[m]:0);
printf("%d\n",ans.to_int()),ans=0; // III
up(0,m,i) if(n>=i) ans=ans+N[i];
printf("%d\n",ans.to_int()),ans=0; // IV
ans=(m>=n);
printf("%d\n",ans.to_int()),ans=0; // V
ans=(n>=m?N[m]:0);
printf("%d\n",ans.to_int()),ans=0; // VI
ans=F[n-1+m]*G[m-1]*G[n];
printf("%d\n",ans.to_int()),ans=0; // VII
ans=(m>=n?F[m]*G[n]*G[m-n]:0);
printf("%d\n",ans.to_int()),ans=0; // VIII
ans=(n>=m?F[n-1]*G[m-1]*G[n-m]:0);
printf("%d\n",ans.to_int()),ans=0; // IX
up(1,m,i){
up(1,n/i,j) P[i*j]=P[i*j]-O[j];
}
int l=1; while(l<n+1) l<<=1;
poly.EXP(l,P,Q);
printf("%d\n",Q[n].to_int()); // X
printf("%d\n",m>=n); // XI
printf("%d\n",n>=m?Q[n-m].to_int():0); // XII
return 0;
}