组合数学之TwelveFold Way
题目传送
LLC
- n个球有标号,m个盒子有标号,每个盒子至多放一个球
- 如果 \(n>m,ans=0\) - 否则为 \(ans=A^n_m=C_m^n\cdot A_n^n\),相当于是先从 \(m\) 个盒子取出 \(n\)个盒子,再全排因为球有编号
LLA
- n个球有标号,m个盒子有标号,盒子没有限制 - 那么每个球都有 \(m\) 种选择 - \(ans=m^n\)
LLB
- n个球有编号,m个盒子有编号,每个盒子至少放一个球 - 如果 \(n<m,ans=0\)
- 否则,我们强制有 \(i\) 个盒子为空,其他盒子乱放,需要容斥一下。
- 首先向LLA一样,答案有 \(m^n\) ,但是这里边含有盒子为空的情况。把一个盒子为空两个盒子为空三个盒子为空...的情况都减掉,但是一个盒子为空里边又包含两个盒子为空三个盒子为空....的情况,会减多,我们加回来两个盒子为空的情况 $$ans=\sum_{i=0}m(-1)i \cdot C_m^i \cdot (m-i)!$$
LUC
- n个球有标号,m个盒子无标号,每个盒子至多放一个球
- 如果 \(n>m,ans=0\) - 否则为 \(ans=1\)
LUB
- n个球有标号,m个盒子无标号,每个盒子至少放一个球
- 我们考虑 DP,设 \(f[i][j]\) 表示前 \(i\) 个球放在 \(j\) 个盒子里的方案数$$f[i][j]=f[i-1][j-1]+j \cdot f[i-1][j]$$
- 因为球有编号,这样顺序DP 起了去重的作用
- 第二类斯特林数的递推公式 $$S_nm=s_{n-1}+m\cdot S_{n-1}^{m}$$
LUA
- n个球有标号,m个盒子无标号,盒子无限制 - 我们可以利用LUB,然后枚举空盒子的数量 $$ans=\sum_{i=1}^mf[n][i]$$
ULC
- n个球无标号,m个盒子有标号,每个盒子至多放一个球
- 如果 \(n>m,ans=0\) - 否则为 \(ans=1\)
ULB
- n个球无标号,m个盒子有标号,每个盒子至少放一个球
- 隔板法 $$ans=C_{n-1}^{m-1}$$
ULA
- n个球无标号,m个盒子有标号,盒子无限制
- 还是隔板法,不过强制几个盒子为空 $$ans=\sum_{i=1}{m}C_{n-1} \cdot C_{m}^{i}$$
- 不要忘记乘强制选盒子的方案数
UUC
- n个球无标号,m个盒子无标号,每个盒子至多放一个球
- 如果 \(n>m,ans=0\)
- 否则为 \(ans=1\)
UUB
- n个球无标号,m个盒子无标号,每个盒子至少放一个球
- 这个不能用隔板法,因为划分出相同数量的盒子,盒子没有编号是相同的。
- 我们考虑DP,设 \(f[i][j]\) 表示前 \(i\) 个球分到 \(j\) 个盒子里
- 为了保证不重,我们保证盒子里球的数量单调不降
- 转移 $$f[i][j]=f[i-1][j-1]+f[i-j][j-1]$$
- 对于当前的球要么新开一个盒子,要么再之前的盒子基础上都放进去一个
- 划分数
UUA
- 对UUB求个和就好了$$ans=\sum_{i=1}^mf[n][i]$$
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ll long long
#define mod 998244353
using namespace std;
string s;
ll fac[1000005],f[1005][1005],infac[1000005];
ll n,m;
ll qpow(ll a,ll b){
ll ans=1ll,res=a;
while(b){
if(b&1) ans=(ans*res)%mod;
res=(res*res)%mod;
b>>=1;
}
return ans;
}
void pre(){
ll nn=max(n,m);
// nn=min(nn,1000000);
fac[0]=1ll;
for(ll i=1;i<=nn;i++) fac[i]=fac[i-1]*i*1ll%mod;
infac[nn]=qpow(fac[nn],mod-2);
for(ll i=nn-1;i>=0;i--) infac[i]=infac[i+1]*(i+1)*1ll%mod;
// for(ll i=0;i<=nn;i++) printf("%lld ",infac[i]);
// cout<<qpow(fac[nn],2)<<endl;
// cout<<endl;
}
void work1(){
if(n<=m) printf("1\n");
else printf("0\n");
return ;
}
void work2(){//LLC
if(n<=m) printf("%lld",fac[m]*infac[m-n]%mod);
else printf("0\n");
// else if(n==m) printf("")
}
void work3(){
if(n<=m) printf("%lld",fac[m]*infac[n]%mod*infac[m-n]%mod);
else printf("0\n");
}
void work4(){//LLB
if(n<m){
printf("0\n");
return ;
}
ll ans=0;
for(ll i=0;i<=m;i++){
if(i%2==0) ans=(ans+fac[m]*infac[m-i]%mod*infac[i]%mod*qpow(m-i,n)%mod)%mod;
else ans=(ans+mod-fac[m]*infac[m-i]%mod*infac[i]%mod*qpow(m-i,n)%mod)%mod;
}
printf("%lld\n",ans);
}
void work5(){//LUB
if(n<m){
printf("0\n");
return ;
}
f[0][0]=1;
//前 i个球放在 j个盒子里的方案数
for(ll i=1ll;i<=n;i++)
for(ll j=1ll;j<=m;j++)
f[i][j]=(f[i-1][j-1]+j*f[i-1][j]%mod)%mod;
printf("%lld\n",f[n][m]);
}
void work6(){//ULB
if(n<m){
printf("0\n");
return ;
}
printf("%lld\n",fac[n-1]*infac[m-1]%mod*infac[n-m]%mod);
}
void work7(){//划分数 UUB
if(n<m){
printf("0\n");
return ;
}
f[0][0]=1ll;
for(ll i=1ll;i<=n;i++)
for(ll j=1ll;j<=m;j++)
f[i][j]=(f[i-1][j-1]+f[max(i-j,0ll)][j])%mod;
printf("%lld",f[n][m]%mod);
}
void work8(){//LLA
printf("%lld",qpow(m,n));
}
void work9(){
f[0][0]=1;
//前 i个球放在 j个盒子里的方案数
for(ll i=1ll;i<=n;i++)
for(ll j=1ll;j<=m;j++)
f[i][j]=(f[i-1][j-1]+j*f[i-1][j]%mod)%mod;
ll ans=0;
for(ll i=1;i<=m;i++)
ans=(ans+f[n][i])%mod;
printf("%lld",ans);
}
void work10(){//ULA
ll ans=0;
for(ll i=0;i<=m;i++)
ans=(ans+fac[m]*infac[i]%mod*infac[m-i]%mod*fac[n-1]%mod*infac[i-1]%mod*infac[n-i]%mod)%mod;
printf("%lld",ans);
}
void work11(){//UUA
f[0][0]=1;
ll ans=0;
for(ll i=1;i<=n;i++)
for(ll j=1;j<=m;j++)
f[i][j]=(f[i-1][j-1]+f[max(0ll,i-j)][j])%mod;
for(ll i=1;i<=m;i++) ans=(ans+f[n][i])%mod;
printf("%lld",ans);
}
int main(){
cin>>s;
scanf("%lld%lld",&n,&m);
if(n<=1000000&&m<=1000000)pre();
// cout<<s<<endl;
// printf("%lld %lld\n",n,m);
if(s=="LUC") work1();
else if(s=="LLC") work2();
else if(s=="UUC") work1();
else if(s=="ULC") work3();
else if(s=="LLB") work4();
else if(s=="LUB") work5();
else if(s=="ULB") work6();//
else if(s=="UUB") work7();
else if(s=="LLA") work8();
else if(s=="LUA") work9();
else if(s=="ULA") work10();//
else if(s=="UUA") work11();
return 0;
}