解法
用容斥原理做
假设P(n,i)
表示n
对夫妇时,有i
队夫妇粘在一起的种类数:
我们选出i
对夫妇,有C(m,i)
种可能,把粘在一起的夫妇算做一个人,总共有2n-i
个人全排列种排法,每个粘在一起的夫妇都有2种排法,所以总共有P(n,i)=2i(2n−i)!CmiP(n,i) = 2^i(2n-i)!C_m^iP(n,i)=2i(2n−i)!Cmi种可能,整个问题的种类数就为:
S(n,m)=P(n,0)−P(n,1)+P(n,2)....=∑i=0m(−1)iP(n,i)=∑i=0m(−2)i(2n−i)!CmiS(n,m) = P(n,0)-P(n,1)+P(n,2)....\\=\sum_{i=0}^m(-1)^iP(n,i)\\=\sum_{i=0}^m(-2)^i(2n-i)!C_m^iS(n,m)=P(n,0)−P(n,1)+P(n,2)....=i=0∑m(−1)iP(n,i)=i=0∑m(−2)i(2n−i)!Cmi
用O(m+n)O(m+n)O(m+n)时间算出这三部分的值,存起来,其中第一部分要变成(MOD−2)i(MOD-2)^i(MOD−2)i,第三部分要变成Cmi=(m−i+1)iMOD−2Cmi−1C_m^i = (m-i+1)i^{MOD-2}C_m^{i-1}Cmi=(m−i+1)iMOD−2Cmi−1
然后再用O(m)O(m)O(m)时间求和就行了
python会超时,改成c++就好了= =
#include <stdio.h>
#include <string>
#include <iostream>
#include <memory.h>
#include <stdlib.h>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
#include <functional>
#define MAXN 100010
#define NINF -100000
#define INF 65536
using namespace std;
typedef long long lld;
int n,m;
const int MOD = 1000000007;
const int minus2 = MOD-2;
int A[MAXN],C[MAXN],B[MAXN<<1];
int maxn,maxm;
int POW(int i,int k) {
int res = 1;
int base = i;
while (k>0) {
if (k&1)
res = (lld)res*base%MOD;
base = (lld)base*base%MOD;
k = k>>1;
}
return res;
}
int solve() {
for(int i=maxm+1;i<=m;++i) {
A[i] = (lld)A[i-1]*minus2%MOD;
}
for(int i=1;i<=m;++i) {
C[i] = (lld)(m-i+1)*POW(i,minus2)%MOD*C[i-1]%MOD;
}
maxm = max(m,maxm);
int UP = (n<<1);
for(int i=(maxn<<1)+1;i<=UP;++i) {
B[i] = (lld)B[i-1]*i%MOD;
}
maxn = max(n,maxn);
int ans = B[n<<1];
for(int i=1;i<=m;++i) {
ans = (ans+(lld)A[i]*B[2*n-i]%MOD*C[i]%MOD)%MOD;
}
return ans;
}
int main() {
// freopen("/Users/huangyuemei/CLionProjects/untitled/C-large-practice.in","r",stdin);
// freopen("/Users/huangyuemei/CLionProjects/untitled/my.out","w",stdout);
A[0] = C[0] = B[0] = 1;
maxn = maxm = 0;
int t;
scanf("%d",&t);
for(auto round=1;round<=t;++round) {
scanf("%d%d",&n,&m);
printf("Case #%d: %d\n",round,solve());
}
}