Description
今天是Jane的生日。Alice和Bob都有一些糖果,于是这两个人就去买一些白色的盒子去包装这些糖果作为Jane的生日礼物。他们将随机地把这些盒子分成两堆,一堆给Alice,一堆给Bob(每堆至少有一个盒子)。
我们知道Alice有 N1 个不同的糖果,Bob有 N2 个相同的糖果(由于Bob很懒,所以他直接买了相同的糖果),然后Alice买的糖果和Bob买的糖果是完全不一样的。他们两人各自把糖果放进盒子里,并且任意一个盒子都不会是空的。Alice和Bob各自有各自包装糖果的规则:
Alice很有个性,她会根据盒子的数量来决定包装糖果的方法。记Alice拥有的盒子数量为 k 。若 k 是个质数,则她会随机地包装糖果(每个糖果都有可能进入任意一个盒子里)。若 k 不是质数,她会往其中的 k−1 个盒子里分别随机放入1个糖果,将剩下的糖果全部放入剩下那个盒子里。
Bob有M种颜料(这M种颜料中没有白色),他将随机地给盒子上色,使得每个盒子都有上色且颜色不一样。然后Bob会随机地包装糖果(每个糖果都可能进入任意一个盒子里)。
问题来了,Jane会收到多少种礼物?
Input
第一行为一个整数T,表示测试数据组数。
对于每组测试数据,第一行为四个整数 N1,N2,M,Q ,分别表示Alice的糖果数目、Bob的糖果数目、Bob的颜色数目、询问个数。
接下来Q行,每行一个整数N,表示Alice和Bob购买的盒子的总数 (2≤N≤M) 。
Output
对于每组测试数据,输出Q行,每行一个整数表示Jane可能收到的礼物的种数。由于答案可能非常大,请将答案对786433取模后输出。
Sample Input
1
4 3 3 2
2
3
Sample Output
3
27
HINT
【样例解释】
Alice的糖果为 A,B,C,D,Bob的糖果为 a,a,a ,Bob的颜色为 C1, C2, C3。盒子初始的颜色为W。
对于n = 2,Jane可以收到3种礼物:
W{A,B,C,D},C1{a,a,a}
W{A,B,C,D},C2{a,a,a}
W{A,B,C,D},C3{a,a,a}
对于n = 3,Jane可以收到27种礼物:
W{A},W{B,C,D},C1{a,a,a}
W{A},W{B,C,D},C2{a,a,a}
W{A},W{B,C,D},C3{a,a,a}
W{B},W{A,C,D},C1{a,a,a}
W{B},W{A,C,D},C2{a,a,a}
W{B},W{A,C,D},C3{a,a,a}
W{C},W{A,B,D},C1{a,a,a}
W{C},W{A,B,D},C2{a,a,a}
W{C},W{A,B,D},C3{a,a,a}
W{D},W{A,B,C},C1{a,a,a}
W{D},W{A,B,C},C2{a,a,a}
W{D},W{A,B,C},C3{a,a,a}
W{A,B},W{C,D},C1{a,a,a}
W{A,B},W{C,D},C2{a,a,a}
W{A,B},W{C,D},C3{a,a,a}
W{A,C},W{B,D},C1{a,a,a}
W{A,C},W{B,D},C2{a,a,a}
W{A,C},W{B,D},C3{a,a,a}
W{A,D},W{B,C},C1{a,a,a}
W{A,D},W{B,C},C2{a,a,a}
W{A,D},W{B,C},C3{a,a,a}
W{A,B,C,D},C1{a},C2{a,a}
W{A,B,C,D},C1{a},C3{a,a}
W{A,B,C,D},C2{a},C1{a,a}
W{A,B,C,D},C2{a},C3{a,a}
W{A,B,C,D},C3{a},C1{a,a}
W{A,B,C,D},C3{a},C2{a,a}
【数据范围与约定】
对于10%的数据: 1≤N1,N2,M,Q≤10
对于50%的数据: 1≤N1,N2,M,Q≤1000
对于所有数据: 1≤N1,N2,M,Q≤105,T≤5
题解:斯特林数+NTT
暴力(40):
对于Alice是质数时相当于把n个本质不同的球放入m个本质相同的盒子中为第一类斯特林数
是合数时相当于从n个球中挑出n-m+1个球放入一个球中的方案数采用隔板法可知为组合数。
对于Bob可以看成把n个相同球放入m个盒子的方案数乘将M个颜色分配给m个盒子的方案数
对于前一部分可用插板法(n-1个空位用m-1个板子隔开)方案数为C[n-1][m-1].
暴力枚举即可。
40分代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
typedef unsigned long long ll;
const int Maxn=3e3+50;
const ll Mod=786433;
bool IsNotPrime[Maxn];
int Prime[Maxn],PrimeCnt;
ll p[Maxn][Maxn],c[Maxn][Maxn];
int n,n1,n2,m,T,Q;
inline int read()
{
char ch=getchar();
int i=0,f=1;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){i=(i<<3)+(i<<1)+ch-'0';ch=getchar();}
return i*f;
}
inline void pre()
{
IsNotPrime[1]=1;
for(int i=2;i<Maxn;i++)
{
if(!IsNotPrime[i])
{
Prime[++PrimeCnt]=i;
}
for(int j=1;j<=PrimeCnt&&i*Prime[j]<Maxn;j++)
{
IsNotPrime[i*Prime[j]]=1;
if(i%Prime[j]==0)break;
}
}
for(int i=0;i<Maxn;i++)
{
c[i][0]=1;
}
for(int i=1;i<Maxn;i++)
for(int j=1;j<Maxn;j++)
{
c[i][j]=(c[i-1][j-1]%Mod+c[i-1][j]%Mod)%Mod;
}
}
inline void Striling()
{
p[0][0]=1;
for(int i=1;i<Maxn;i++)
for(int j=1;j<Maxn;j++)
p[i][j]=(p[i-1][j-1]%Mod+1LL*p[i-1][j]%Mod*j)%Mod;
}
inline ll calc1(int n1,int i)
{
if(i==n1) return 1;
if(!IsNotPrime[i])
{
return p[n1][i]%Mod;
}
else return c[n1][i-1]%Mod;
}
inline ll calc2(int n2,int i)
{
return (c[n2-1][i-1]%Mod*c[m][i]%Mod)%Mod;
}
int main()
{
pre();
Striling();
T=read();
while(T--)
{
n1=read(),n2=read(),m=read(),Q=read();
while(Q--)
{
ll ans=0;
n=read();
for(int i=1;i<n;i++)
{
if(i>n1||n-i>n2)continue;
(ans+=(calc1(n1,i)%Mod*calc2(n2,n-i)%Mod)%Mod)%=Mod;
}
cout<<ans%Mod<<endl;
}
}
}
正解(100):
第二类斯特林数的大数求法:
F[i]=1i!∑k=0i(−1)kCki∗(i−k)n
然后转化一下:
F[i]=∑k=0i(−1)k(i−k)nk!(i−k)!
F[i]=∑k=0i(−1)kk!×(i−k)n(i−k)!
正解代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int Mod=786433;
const int G=13;
const int Maxn=1e6+50;
inline int read()
{
char ch=getchar();
int i=0,f=1;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){i=(i<<3)+(i<<1)+ch-'0';ch=getchar();}
return i*f;
}
ll A[Maxn],B[Maxn],C[Maxn],Alice[Maxn],Bob[Maxn],Ans[Maxn],fac[Maxn],inv[Maxn];
int n1,n2,m,T,Q;
bool IsNotPrime[Maxn];
int Prime[Maxn],Primecnt;
inline ll ksm(ll a,ll b)
{
ll tmp=1;
while(b)
{
if(b&1)tmp=(tmp*a)%Mod;
a=(a*a)%Mod;
b>>=1;
}
return tmp;
}
struct NTT
{
ll pos[Maxn],k;
inline void Init(int len)
{
for(k=1;k<len+2000;k<<=1);//数据会卡k<<1
for(ll i=1;i<k;i++)
pos[i]=(i&1)?((pos[i>>1]>>1)^(k>>1)):(pos[i>>1]>>1);
}
inline void ntt(ll *a)
{
for(int i=1;i<k;i++)
if(i<pos[i])swap(a[i],a[pos[i]]);
for(int m1=1,m2;m1<k;m1<<=1)
{
m2=m1<<1;
ll wn=ksm(G,(Mod-1)/m2);
for(int i=0;i<k;i+=m2)
{
ll w=1;
for(int j=0;j<m1;j++)
{
ll &A1=a[i+j],&B1=a[i+j+m1],t=1LL*w*B1%Mod;
B1=(A1-t+Mod)%Mod;
A1=(A1+t)%Mod;
w=(w*wn)%Mod;
}
}
}
}
inline void muitiply(ll *a,ll *b,ll *c)
{
ntt(a),ntt(b);
for(ll i=0;i<k;i++)c[i]=(a[i]*b[i])%Mod;
reverse(c+1,c+k);
ntt(c);
ll Inv=ksm(k,Mod-2);
for(ll i=0;i<k;i++)c[i]=(1LL*Inv*c[i])%Mod;
}
}ntt;
inline void Pre()
{
IsNotPrime[1]=1;
for(int i=2;i<Maxn;i++)
{
if(!IsNotPrime[i])
{
Prime[++Primecnt]=i;
}
for(int j=1;j<=Primecnt&&i*Prime[j]<Maxn;j++)
{
IsNotPrime[i*Prime[j]]=1;
if(i%Prime[j]==0)break;
}
}
fac[0]=inv[0]=1;
for(int i=1;i<Maxn;i++)fac[i]=fac[i-1]*i%Mod;
for(int i=1;i<Maxn;i++)inv[i]=inv[i-1]*ksm(i,Mod-2)%Mod;
}
int main()
{
//freopen("lx.in","r",stdin);
Pre();
scanf("%d",&T);
while(T--)
{
memset(A,0,sizeof(A));
memset(B,0,sizeof(B));
scanf("%d%d%d%d",&n1,&n2,&m,&Q);
ntt.Init(n1+n2);
A[0]=1;
for(int i=1;i<=n1;i++)A[i]=A[i-1]*i%Mod;
for(int i=0;i<=n1;i++)A[i]=B[i]=ksm(A[i],Mod-2);
for(int i=1;i<=n1;i+=2)A[i]=A[i]*(Mod-1)%Mod;
for(int i=0;i<=n1;i++)B[i]=B[i]*ksm(i,n1)%Mod;
ntt.muitiply(A,B,C);
memset(Alice,0,sizeof(Alice));
memset(Bob,0,sizeof(Bob));
memset(Ans,0,sizeof(Ans));
for(int i=1;i<=n1;i++)
{
if(!IsNotPrime[i])Alice[i]=C[i];
else if(i==n1)Alice[i]=1;
else Alice[i]=fac[n1]*inv[i-1]%Mod*inv[n1-i+1]%Mod;
}
for(int i=1;i<=n2;i++)
Bob[i]=fac[m]*inv[i]%Mod*inv[m-i]%Mod*fac[n2-1]*inv[i-1]%Mod*inv[n2-i]%Mod;
ntt.muitiply(Alice,Bob,Ans);
while(Q--)
{
int x;
scanf("%d",&x);
printf("%lld\n",Ans[x]);
}
}
}