@lychee123
2017-08-19T02:39:57.000000Z
字数 1485
阅读 1377
组合数 容斥
题意
姓和名由两个长度均为的字符串组成。这些字符串中的字符在个给定字符中选择。但要求姓和名不能出现相同的字符。 问有多少种不同的构成这个姓名的方法。
组测试数据
样例
Sample Input
2
3 2
2 3Sample Output
2
18
分析
我们从个元素中选择个元素 来构成姓,此时选法为 我们将组合数预处理出来
LL c[maxn][maxn];void init(){c[0][0]=1;for(int i=1;i<maxn;i++){c[i][0]=1;for(int j=1;j<maxn;j++){c[i][j]=c[i-1][j-1]+c[i-1][j];if(c[i][j]>=mod)c[i][j]-=mod;//对结果取模。加减法比乘除法快40倍。尽量选择加减。(细节)}}}
姓:
每次 的时候对于姓有 种排列此时会有重复。因为这时候会出现并没有将这种元素用完的情况。就会与之前已经加过的情况重复。这种时候就应该想到容斥了。
名:
处理方法和姓一样选择的时候是我们的总的公式为
对于f[i]的处理就用到了容斥(先加后减,最开始flag=1,然后flag=-flag,一直循环下去)
(预处理)
边界之类的要注意,还有取模部分也要小心,不要取漏了
代码
#include<bits/stdc++.h>using namespace std;typedef long long LL;const int maxn=2000+5;const LL mod=1e9+7;LL c[maxn][maxn];void init(){c[0][0]=1;for(int i=1;i<maxn;i++){c[i][0]=1;for(int j=1;j<maxn;j++){c[i][j]=c[i-1][j-1]+c[i-1][j];if(c[i][j]>=mod)c[i][j]-=mod;}}}LL in[maxn],f[maxn];void getf(int n,int m){for(int i=0;i<=m;i++){in[i]=1;for(int j=1;j<=n;j++)(in[i]*=i)%=mod;}for(int i=1;i<=m;i++){int flag=1;f[i]=0;for(int j=min(i,n);j>=0;j--){f[i]+=flag*c[i][j]*in[j]%mod;f[i]%=mod;flag=-flag;}if(f[i]<0)f[i]+=mod;}}int main(){init();int t;scanf("%d",&t);while(t--){int n,m;scanf("%d%d",&n,&m);getf(n,m);LL ans=0;for(int i=1;i<=min(n,m-1);i++){for(int j=1;j<=min(n,m-1)&&j+i<=m;j++){ans+=(c[m][i]*f[i])%mod*(c[m-i][j]*f[j]%mod)%mod;//取模取到位if(ans>=mod)ans-=mod;}}printf("%lld\n",ans);}return 0;}