BJ模拟 生日礼物(斯特林数+NTT)

Description

     今天是Jane的生日。AliceBob都有一些糖果,于是这两个人就去买一些白色的盒子去包装这些糖果作为Jane的生日礼物。他们将随机地把这些盒子分成两堆,一堆给Alice,一堆给Bob(每堆至少有一个盒子)。

      我们知道Alice N1 不同的糖果,Bob N2 相同的糖果(由于Bob很懒,所以他直接买了相同的糖果),然后Alice买的糖果和Bob买的糖果是完全不一样的。他们两人各自把糖果放进盒子里,并且任意一个盒子都不会是空的。AliceBob各自有各自包装糖果的规则:

      Alice很有个性,她会根据盒子的数量来决定包装糖果的方法。记Alice拥有的盒子数量为 k 。若 k 是个质数,则她会随机地包装糖果(每个糖果都有可能进入任意一个盒子里)。若 k 不是质数,她会往其中的 k−1 个盒子里分别随机放入1个糖果,将剩下的糖果全部放入剩下那个盒子里。

     BobM种颜料(这M种颜料中没有白色),他将随机地给盒子上色,使得每个盒子都有上色且颜色不一样。然后Bob会随机地包装糖果(每个糖果都可能进入任意一个盒子里)。

     问题来了,Jane会收到多少种礼物?

Input

     第一行为一个整数T,表示测试数据组数。

      对于每组测试数据,第一行为四个整数 N1,N2,M,Q ,分别表示Alice的糖果数目、Bob的糖果数目、Bob的颜色数目、询问个数。

      接下来Q行,每行一个整数N,表示AliceBob购买的盒子的总数 (2≤NM) 

Output

      对于每组测试数据,输出Q行,每行一个整数表示Jane可能收到的礼物的种数。由于答案可能非常大,请将答案对786433取模后输出。

Sample Input

1

4 3 3 2

2

3

Sample Output

3

27

HINT

【样例解释】

Alice的糖果为 A,B,C,DBob的糖果为 a,a,a Bob的颜色为 C1, C2, C3。盒子初始的颜色为W

 

对于n = 2Jane可以收到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 = 3Jane可以收到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(ik)n


然后转化一下:

F[i]=k=0i(−1)k(ik)nk!(ik)!

 

F[i]=k=0i(−1)kk!×(ik)n(ik)!

 

正解代码:

#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]);
		}
	}
}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值