多校8.11 斯特林数,容斥

来源:互联网 发布:java里氏替换原则 编辑:程序博客网 时间:2024/05/24 02:21

题意是一个人有姓和名,都是有n个字符组成的。要求一个人的姓何名不能出现相同的字符。现在有m个字符,问你一共可以有多少不同的人。

首先我们先学习下斯特林数。

第一类Stirling数s(p,k)

第一类Stirling数的含义:将p个物体排成k个非空循环排列的种类数。

s(p,k)=s(p-1,k-1)+s(p-1,k)*(p-1)。1<=k<=p-1 s(p,0)=0,s(p,p)=1

递推关系的说明:

考虑第p个物品,p可以单独构成一个非空循环排列,这样前p-1种物品构成k-1个非空循环排列,方法数为s(p-1,k-1);

也可以前p-1种物品构成k个非空循环排列,而第p个物品插入第i个物品的左边,这有(p-1)*s(p-1,k)种方法。

第二类Stirling数s(p,k)

含义:将p个物体划分成k个不可分辨的集合的方法数。

k!s(p,k)是将p个物体划分成k个可分别(有编号)的集合的方法数。

s(p,k)=s(p-1,k-1)+s(p-1,k)*k; s(p,p)=1;s(p,0)=0;

那么对于这道题,我们可以枚举姓和名的字符数分别为i,j;

那么答案为C(m,i)*F(n,i)*C(m-i,j)*F(n,j)。F(n,i)表示在这n个位置中放i个字符的方法数。可以知道F(n,i)=i!*s(n,i)

也可以根据容斥原理 F(n,x)=xn(x1)(x1)n+(x2)(x2)n(x3)(x3)n+...

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll s[2010][2010],C[2010][2010],pow[2010];
void init()
{
memset(s,0,sizeof(s));
s[1][1]=1;
for(int i=2;i<=2000;i++)
{
for(int j=1;j<=i;j++)
{
s[i][j]=j*s[i-1][j]+s[i-1][j-1];
if(s[i][j]>=mod)
 s[i][j]%=mod;
}
}
C[0][0]=1;C[1][0]=1;C[1][1]=1;
for(int i=2;i<=2000;i++)
{
for(int j=0;j<=i;j++)
{
   C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
}
void powd()
{
pow[1]=1;
for(int i=2;i<=2000;i++)
{
pow[i]=(pow[i-1]*i)%mod;
}
}
int main()
{
int t,n,m;
scanf("%d",&t);
init(); 
powd();
while(t--)
{
scanf("%d%d",&n,&m);
ll ans=0,cnt=0;
for(int i=1;i<m;i++)
{
for(int j=1;j<=m-i;j++)
{
ll a=((C[m][i]*s[n][i])%mod*pow[i])%mod;
ll b=((C[m-i][j]*s[n][j])%mod*pow[j])%mod;
ans+=(a*b)%mod;
ans=ans%mod;
}
}
printf("%I64d\n",ans);
}
}






原创粉丝点击