DP+容斥
然而这题还是令我很害怕。
最值问题的DP一般考虑排序之后从小到大或从大到小,一个一个地考虑。
对于这题,对A,B排序,记next[i]=j,表示最大的j满足B[j] < A[i],不难想到方程f[i][j]表示做到A的第i个,其中已经有j对满足A>B,然后转移
f[i][j] = f[i-1][j] + f[i-1][j-1] * (next[i] - j + 1)
但是这是错的,因为我们只钦定了有j对大于,并不能确定剩下的n-j对就一定是小于。一般想到这里就觉得这个DP没用了。然而这题妙在可以直接容斥减去不合法状态。。。
记g[i][j]表示做到A的第i位,恰好有j对满足A>B。
我们考虑f[n][j]中重复出现的东西到底出现了多少次。
对于恰好j对的方案,一定是不重不漏地都被统计了,因为这j对一定恰好被钦定了。
对于恰好j+1的方案,有C(j+1,j)种方案能选出j个来被钦定,因此它被统计了C(j+1,j)。
实际上恰好k的方案就统计了C(k,j)次。我们把g乘上这个系数减掉即可。
#include<cstdio>
#include<algorithm>
#define MOD 1000000009
#define N 2005
using namespace std;
namespace runzhe2000
{
typedef long long ll;
const int INF = 1<<30;
int a[N], b[N], next[N], f[N][N], g[N], c[N][N], n, k, fac[N];
void init()
{
c[0][0] = 1; fac[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]) % MOD;
fac[i] = (ll)fac[i-1] * i % MOD;
}
}
void main()
{
scanf("%d%d",&n,&k);
if((n+k)&1){puts("0"); return;}
init();
for(int i = 1; i <= n; i++) scanf("%d",&a[i]); sort(a+1, a+1+n);
for(int i = 1; i <= n; i++) scanf("%d",&b[i]); sort(b+1, b+1+n); b[n+1] = INF;
for(int i = 1; i <= n; i++)
{
int p = next[i-1];
for(; b[p+1] < a[i]; p++);
next[i] = p;
}
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] + (ll)f[i-1][j-1] * (next[i] - j + 1)) % MOD;
}
for(int i = n; i; i--)
{
f[n][i] = (ll)f[n][i] * fac[n-i] % MOD;
for(int j = n; j > i; j--)
{
f[n][i] = (f[n][i] - (ll)g[j] * c[j][i]) % MOD;
}
g[i] = f[n][i];
}
printf("%d\n",(g[(n+k)/2]+MOD) % MOD);
}
}
int main()
{
runzhe2000::main();
}