題意: 有N對couple坐在一排,其中指定的M對newlywed couple中,每個couple不能挨着坐。問一共多少種就坐方式。
結果需要mod 1e7,肯定是個算排列組合數的問題。很容易聯想到容斥原理。但是開始糾結容斥原理需要計算所有的子集,總共要算2^N個,肯定會逾時。最後半小時才想起來,對于大小相同的子集,他們對應的組合數是一樣的,是以每個size隻要算一次就行了。然而中途出現了Bug木有時間調了。T^T
令A_i表示couple i (belong to M couples) 坐在一塊的方式有幾個。那麼invalid的坐法個數是 z=|A_0 \cup A_1 \cup ... \cupA_M|,合理的坐法是2^{2N}-z。計算z本質是個集合并集的問題,可以用容斥原理。
以size=n的|A_i \cup...\cup A_j |為例,考慮保證集合中的n個newlywed couple i,..., j是坐一塊的,總的組合數是 C(M,n) * C(2*N-n,n) * n! * 2^n * (2*N-n*2)!。其中,C(M,n)是M中size=n的集合的個數。C(2*N-n,n)是從2*N個位置中選出n個2人的位子(兩個相鄰的位子)放置n個couple,也就等同于2N-n中選擇n個位置,每個位置放一個couple。n!是因為集合中的這些newlywed couple之間map到n個位置會有不用的map方式。2^n是每對newlywed couple兩個人的位置可以互換。(2*N-n*2)!是安排集合之外的couple的位置,這些couple可能兩個人相鄰也可能不相鄰。
因為公式中仍然可能讓n的集合之外的newlywed couple坐一塊,是以多加的需要用容斥原理減回來。
開始結果總是出現負數,還以為公式推錯了。後來發現a%mod-b%mod這種會導緻負數出現,因為取過mod之後,可能前一項比後一項小。應該寫成 a%mod+mod-b%mod o(╯□╰)o之前才過很多次坑了,這次又忘了。
桑心,這次題目也沒以前難,要是少繞彎子就可以AK了。T_T
#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
//Kickstart Round H Problem C
const int maxn=200010;
int N;
int M;
const int mod=1000000007;
int T;
long long ans;
long long fact[maxn];
long long Pow(long long a,long long b)
{
long long s=1;
long long t=1;
while(b)
{
if(b&t)
{
s=(s*a)%mod;
}
a=(a*a)%mod;
b=b>>1;
}
return s;
}
long long twopow[maxn];
void getfac()
{
fact[0]=1;
for(int i=1; i<maxn; i++)
{
fact[i]=(fact[i-1]*i)%mod;
// if(i<5)
// {
// cout<<i<<" "<<fact[i]<<endl;
// }
}
}
long long inv(long long a) {
return Pow(a, mod - 2);
}
long long Comb(int n,int m)
{
if(n==m)
{
return 1;
}
if(m==0)
{
return 1;
}
if (n<m)
{
return 0;
}
return ((fact[n]*inv(fact[m]))%mod*inv(fact[n-m])%mod)%mod;
}
int main()
{
// freopen("input.txt","r",stdin);
freopen("C-large-practice.in","r",stdin);
freopen("C.txt","w",stdout);
clock_t START_TIME;
clock_t FINISH_TIME;
START_TIME=clock();
scanf("%d",&T);
memset(fact,0,sizeof(fact));
getfac();
memset(twopow,0,sizeof(twopow));
twopow[0]=1;
for(int i=1;i<maxn;i++)
{
twopow[i]=2*(twopow[i-1]%mod);
twopow[i]%=mod;
}
for(int ca=1;ca<=T;ca++)
{
cin>>N>>M;
N=N*2;
long long sum=0;
ans=0;
int sign=1;
for(int i=1;i<=M;i++)
{
long long tmp=Comb(M,i);
tmp=tmp*fact[i];
tmp%=mod;
tmp=tmp*(Comb(N-i,i))%mod;
tmp%=mod;
tmp=tmp*twopow[i]%mod;
tmp%=mod;
tmp=tmp*(fact[N-i*2]%mod);
tmp%=mod;
// cout<<Comb(M,i)<<" "<<Comb(N-i,i)<<" "<<twopow[i]<<" "<<fact[N-i*2]<<endl;
if(sign==1)
{
sum+=tmp;
}
else if(sign==-1)
{
sum-=tmp;
}
sum%=mod;
sign=-sign;
// cout<<"i "<<i<<" "<<tmp<<endl;
}
//cout<<N<<" "<<M<<endl;
// cout<<fact[N]<<" "<<sum<<endl;
ans=(fact[N]%mod)+mod-(sum%mod);//when there is minus in using mod, remember to +mod, otherwise, negative number may occur
ans%=mod;
printf("Case #%d: %lld\n",ca,ans);
cerr<<"finish case "<<ca<<endl;
}
FINISH_TIME=clock();
cerr<<1.0*(FINISH_TIME-START_TIME)/CLOCKS_PER_SEC <<" (s) "<<endl;
return 0;
}