HDU 6143 Killer Names(组合数+第二类Stirling数)

来源:互联网 发布:python 登录知乎 编辑:程序博客网 时间:2024/04/19 11:17

写在前面

比赛的时候做出来这个方法的某个版本,结果因为TLE放弃了……以为这个方法是错的所以对着官方题解想recoding,结果又发现了组合数和阶乘的打表姿势……(눈 눈」∠)_
当时要是想到也不至于爆零了~唉~
Link:http://acm.hdu.edu.cn/showproblem.php?pid=6143

题意

有两个长度为n的串,m种字符,要求每个字符不能同时在两个串里出现,问这样的两个串有多少种

思路

因为有两个串,所以我们可以先确定第一个串,然后再去找第二个串的组合。
第一个串只由一种字符组成时,先选出一种字符,然后第一个串只有一种排列,第二个串可以由剩下的m1种字符组成。

C1m(m1)n

第二个串只由两种字符组成,先选出两种字符,然后第一个串相当于是 有2种数字放在n个位置上的排列方案数,第二个串可以由剩下的m2种字符组成。
对于x种字符放n个位置上的关系,可以想到有x个不同的盒子,n个不同的球放进去,且盒子不能为空。首先把n的不同的球分成x堆,可以联想到第二类Stirling数,再Axx排列一下即可。
C2ms[n][2]2!(m2)n

iCims[n][i]i!(mi)n

i取值为1 min(n,m1),将每一项相加即可。

注意

务必要将组合数和Stirling数 阶乘数打表,不然就会TLE……
以及每一步都要取模!

代码

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>#include <set>#include <vector>using namespace std;typedef long long LL;const LL p = 1e9 + 7;LL n,m;LL s[2010][2010];//存放要求的Stirling数LL C[2010][2010];LL f[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]=s[i-1][j-1]+j*s[i-1][j];            if(s[i][j]>=p)                s[i][j]%=p;        }}LL fa(){    f[1] = 1;    for(LL i = 2; i <= 2001; i++){        f[i] = (f[i-1]%p * i%p)%p;    }}LL quick_mod(LL a, LL b) {    LL ans = 1;    a %= p;    while(b) {        if(b & 1) {            ans = ans * a % p;            b--;        }        b >>= 1;        a = a * a % p;    }    return ans;}int main() {    fa();    init();    C[0][0] = 1;    for(int i = 1; i <= 2001; i++){        C[i][0] = 1;        for(int j = 1; j <= i; j++){            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % p;        }    }    int T;    scanf("%d", &T);    while(T--) {        scanf("%lld %lld", &n, &m);        LL fin = min(n, m -1);        LL ans = 0;        for(LL i = 1; i <= fin; i++) {            ans += ((((C[m][i] % p) * (s[n][i] % p)  %p) * (f[i] % p) % p)*(quick_mod(m-i,n)%p)%p);            ans %= p;        }        printf("%lld\n",ans);    }    return 0;}

补充

第一类Stirling数:

定理:第一类Stirling数s(p,k)计数的是把p个对象排成k个非空循环排列的方法数。
递推公式:
S(p,p)=1(p0)
S(p,0)=0(p1)
S(p,k)=(p1)S(p1,k)+S(p1,k1)(1kp1)

long long s[maxn][maxn];//存放要求的第一类Stirling数  const long long mod=1e9+7;//取模  void init()//预处理  {      memset(s,0,sizeof(s));      s[1][1]=1;      for(int i=2;i<=maxn-1;i++)          for(int j=1;j<=i;j++)      {          s[i][j]=s[i-1][j-1]+(i-1)*s[i-1][j];          if(s[i][j]>=mod)              s[i][j]%=mod;      }  }  

第二类Stirling数:

定理:第二类Stirling数S(p,k)计数的是把p元素集合划分到k个不可区分的盒子里且没有空盒子的划分个数。
递推公式:
S(p,p)=1(p0)
S(p,0)=0(p1)
S(p,k)=kS(p1,k)+S(p1,k1)(1kp1)

long long s[maxn][maxn];//存放要求的Stirling数  const long long mod=1e9+7;//取模  void init()//预处理  {      memset(s,0,sizeof(s));      s[1][1]=1;      for(int i=2;i<=maxn-1;i++)          for(int j=1;j<=i;j++)      {          s[i][j]=s[i-1][j-1]+j*s[i-1][j];          if(s[i][j]>=mod)              s[i][j]%=mod;      }  }

组合数递推公式

C0i=1
Cji=Cj1i1+Cji1
快速的递推出来~

原创粉丝点击