题目描述:
两种元素
a,b
a
,
b
,各有n个,两两配对,求
a[i]>b[i]
a
[
i
]
>
b
[
i
]
的组数恰好比
a[i]<b[i]
a
[
i
]
<
b
[
i
]
的组数大
k
k
的方案数
分析:
有点受这道题的启发
由题面可得,的组数应该为
n+k2
n
+
k
2
(在这里显然有个特判,即
n+k
n
+
k
为奇数时,答案一定为 0 )
现在的问题是如何计算 “合法组数至少为 i i ” 的方案数
把和
b
b
都排序(从小到大)
设,表示
a
a
数组前位中至少有
j
j
个合法组
设表示最大的
j
j
满足
注意:
这个状态只考虑合法组,并不能构造合法的序列
也就是说我们只能计算出
j
j
个合法组的情况
最后答案是:(剩下的位置随意排列)
然而 f f 数组得到的是 “合法组数至少为” 的方案数,考虑容斥
设
f′[i][j]
f
′
[
i
]
[
j
]
表示
i
i
个糖果,有组糖果数 > 药片数,剩下的都是药片数 > 糖果数(即正确答案)
(n−j)! ( n − j ) ! 是枚举后面 n−j n − j 可能的方案, f′[n][j]∗C(j,i) f ′ [ n ] [ j ] ∗ C ( j , i ) 表示 f[n][i] f [ n ] [ i ] 中实际有 j j 组药片数 > 糖果数却被计入的数量
时间复杂度为 O(n2) O ( n 2 )
tip
式子是式子,代码是代码
f[i][j]=(f[i-1][j]+f[i-1][j-1]*max(nxt[i]-j+1,0)%p)%p;
不允许有负数出现:max(nxt[i]-j+1,0)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const ll p=1e9+9;
const int N=2005;
int n,m,a[N],b[N],nxt[N];
ll c[N][N],jc[N],f[N][N],g[N];
void prepare() {
jc[0]=1; jc[1]=1;
for (int i=2;i<=n;i++) jc[i]=(jc[i-1]*(ll)i)%p;
c[0][0]=1;
for (int i=1;i<=n;i++) {
c[i][0]=1;
for (int j=1;j<=i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
}
int j=0;
for (int i=1;i<=n;i++) { //最大的j满足糖果[i]>药片[j]
while (b[j+1]<a[i]&&j+1<=n) j++;
nxt[i]=j;
}
}
void dp() {
f[0][0]=1;
for (int i=1;i<=n;i++) {
f[i][0]=1;
for (int j=1;j<=i;j++)
f[i][j]=(f[i-1][j]+f[i-1][j-1]*max(nxt[i]-j+1,0)%p)%p;
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++) scanf("%d",&b[i]);
sort(a+1,a+1+n); sort(b+1,b+1+n);
if ((n+m)&1) {
printf("0\n");
return 0;
}
prepare();
dp();
int o=(n+m)/2;
for (int i=n;i>=o;i--) {
g[i]=(f[n][i]*jc[n-i])%p;
for (int j=i+1;j<=n;j++)
g[i]=(g[i]-g[j]*c[j][i]%p+p)%p;
}
printf("%lld",g[o]);
return 0;
}