Description
Solution
先别管分差,考虑这个分差算出来第一个队分别得多少分。
此处有一个极其精妙的
dp
先把两个队从小到大排序。
设
f[i][j]
表示做到第
i
个人,第一队赢了
这个明显DP
做完后,剩下
n−j
个人,那么设
g[j]=f[n][j]×(n−j)!
,剩下的人随便打。
那么
g[i]
就是至少赢了
j
场。
设
那么
答案是错的。
为什么?因为有重复。
1表示赢,X表示不清楚
我们只看后三位的
f[4][2]
X1X1
X11X
XX11
转移出
g
,第一位暂时不管。
X111
X111
h[3]=1
,第一位不管。
三种情况都有可能打出同一种方案,所以
h[i]=g[i]−∑j>ih[j]×??
观察上面,??不就是
Cj−ij
么,因为剩下的所有
j−i
个都有可能打出同一种。
所以
h[i]=g[i]−∑j>ih[j]×Cj−ij=g[i]−∑j>ih[j]×Cij
Code
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 2005
#define mo 1000000007
#define LL long long
using namespace std;
int n,m;
LL ans,a[N],b[N],f[N][N],g[N],h[N],js[N];
LL ksm(LL k,LL n)
{
if(n==1) return k%mo;
if(n==0) return 1;
LL s=ksm(k,n/2);
return (n%2)?(s*s%mo*k)%mo:(s*s)%mo;
}
LL zh(LL m,LL n)
{
return(js[n]*ksm(js[m],mo-2)%mo*ksm(js[n-m],mo-2))%mo;
}
int main()
{
cin>>n>>m;
if((n+m)%2!=0)
{
cout<<0;
return 0;
}
int p1=(n+m)/2,p2=(n-m)/2;
js[0]=1;
fo(i,1,n) scanf("%lld",&a[i]),js[i]=js[i-1]*i%mo;
fo(i,1,n) scanf("%lld",&b[i]);
sort(a+1,a+n+1);
sort(b+1,b+n+1);
memset(f,0,sizeof(f));
LL p=0;
f[0][0]=1;
fo(i,1,n)
{
while(b[p+1]<a[i]&&p<n) p++;
fo(j,0,i)
{
f[i][j]=f[i-1][j];
LL j1=j;
if(j>0&&p>=j) f[i][j]=(f[i][j]+f[i-1][j-1]*(p-j1+1)%mo)%mo;
}
}
fod(i,n,0)
{
g[i]=(f[n][i]*js[n-i])%mo;
h[i]=g[i];
fo(j,i+1,n) h[i]=(h[i]-h[j]*zh(i,j)%mo+mo)%mo;
}
if(p1==p2) cout<<(h[p1]%mo);
else cout<<(h[p1]+h[p2])%mo;
}