[关闭]
@lychee123 2017-08-19T10:39:57.000000Z 字数 1485 阅读 1116

组合数+容斥

组合数 容斥


HDU 6143:Killer Names


题意

姓和名由两个长度均为的字符串组成。这些字符串中的字符在个给定字符中选择。但要求姓和名不能出现相同的字符。 问有多少种不同的构成这个姓名的方法。
组测试数据

样例

Sample Input
2
3 2
2 3

Sample Output
2
18

分析

我们从个元素中选择个元素 来构成姓,此时选法为 我们将组合数预处理出来

  1. LL c[maxn][maxn];
  2. void init()
  3. {
  4. c[0][0]=1;
  5. for(int i=1;i<maxn;i++)
  6. {
  7. c[i][0]=1;
  8. for(int j=1;j<maxn;j++)
  9. {
  10. c[i][j]=c[i-1][j-1]+c[i-1][j];
  11. if(c[i][j]>=mod)
  12. c[i][j]-=mod;//对结果取模。加减法比乘除法快40倍。尽量选择加减。(细节)
  13. }
  14. }
  15. }

姓:
每次 的时候对于姓有 种排列此时会有重复。因为这时候会出现并没有将这种元素用完的情况。就会与之前已经加过的情况重复。这种时候就应该想到容斥了。
名:
处理方法和姓一样选择的时候是

我们的总的公式为

对于f[i]的处理就用到了容斥(先加后减,最开始flag=1,然后flag=-flag,一直循环下去)
(预处理)

边界之类的要注意,还有取模部分也要小心,不要取漏了

代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const int maxn=2000+5;
  5. const LL mod=1e9+7;
  6. LL c[maxn][maxn];
  7. void init()
  8. {
  9. c[0][0]=1;
  10. for(int i=1;i<maxn;i++)
  11. {
  12. c[i][0]=1;
  13. for(int j=1;j<maxn;j++)
  14. {
  15. c[i][j]=c[i-1][j-1]+c[i-1][j];
  16. if(c[i][j]>=mod)
  17. c[i][j]-=mod;
  18. }
  19. }
  20. }
  21. LL in[maxn],f[maxn];
  22. void getf(int n,int m)
  23. {
  24. for(int i=0;i<=m;i++)
  25. {
  26. in[i]=1;
  27. for(int j=1;j<=n;j++)
  28. (in[i]*=i)%=mod;
  29. }
  30. for(int i=1;i<=m;i++)
  31. {
  32. int flag=1;
  33. f[i]=0;
  34. for(int j=min(i,n);j>=0;j--)
  35. {
  36. f[i]+=flag*c[i][j]*in[j]%mod;
  37. f[i]%=mod;
  38. flag=-flag;
  39. }
  40. if(f[i]<0)
  41. f[i]+=mod;
  42. }
  43. }
  44. int main()
  45. {
  46. init();
  47. int t;
  48. scanf("%d",&t);
  49. while(t--)
  50. {
  51. int n,m;
  52. scanf("%d%d",&n,&m);
  53. getf(n,m);
  54. LL ans=0;
  55. for(int i=1;i<=min(n,m-1);i++)
  56. {
  57. for(int j=1;j<=min(n,m-1)&&j+i<=m;j++)
  58. {
  59. ans+=(c[m][i]*f[i])%mod*(c[m-i][j]*f[j]%mod)%mod;//取模取到位
  60. if(ans>=mod)
  61. ans-=mod;
  62. }
  63. }
  64. printf("%lld\n",ans);
  65. }
  66. return 0;
  67. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注