【BZOJ3622】已经没有什么好害怕的了,DP+容斥原理

传送门
写在前面:sunshine爷的模拟题之一,妈妈以后只要是模拟赛我再也不用万能库了啊啊啊啊(你们懂得)
思路:
1.首先想到(n-k) mod 2==1与k<=n这两个条件都应该满足,不然就是0
2.升序排两遍,然后对于a[i],我们记录b数组中所有比它小的元素的个数,记为pos[i]
3.f[i][j]指前i个元素中a数组至少有j个元素比b数组中元素大的方案数,显然我们有
这里写图片描述
初始状态为f[i][0]=1(i=1,2,…,n)
4. f’[i][j],表示到第i个元素,糖果>药片的组数为j,且剩下的为 糖果<药片的方案数
这里写图片描述
由于n<=2000,所以直接O(n)处理阶乘,对于组合数,我们可以处理阶乘关于1000000009的逆元,但个人觉得比较麻烦,所以直接递推C好了


#include<bits/stdc++.h>
#define mod 1000000009
#define LL long long
using namespace std;
int n,k;
int a[2010],b[2010],pos[2010];
LL f[2010][2010],c[2010][2010],fac[2010];
main()
{
    scanf("%d%d",&n,&k);
    if ((n-k)&1||k>n) printf("0"),exit(0);
    //1
    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+n+1);
    sort(b+1,b+n+1);
    int j=1;
    for (int i=1;i<=n;i++)
    {
        while (b[j]<a[i]&&j<=n) j++;
        if (j<=n)pos[i]=j-1;
        else pos[i]=n;
    }
    //2
    fac[0]=1;
    for (int i=1;i<=n;i++) fac[i]=i*fac[i-1]%mod;
    for (int i=0;i<=n;i++) c[i][0]=1;
    for (int i=1;i<=n;i++)
    for (int j=1;j<=i;j++)
    c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    for (int i=0;i<=n;i++) f[i][0]=1;
    for (int i=1;i<=n;i++)
    for (int j=1;j<=i;j++)
        f[i][j]=(f[i-1][j]+f[i-1][j-1]*(pos[i]-(j-1))%mod)%mod;
 //3    
    for (int i=n;i>=(k+n)/2;i--)
    {
        f[n][i]=f[n][i]*fac[n-i]%mod;
        for (int j=i+1;j<=n;j++)
        f[n][i]=(f[n][i]-f[n][j]*c[j][i]%mod)%mod;
        while (f[n][i]<0) f[n][i]+=mod;
    }
   //4
    printf("%d",f[n][(k+n)/2]);//我们求出的是前i个元素中a比b多j个的方案,而想要的是“a比b多的”比“b比a多的”多k个(好拗口),简单列一下方程就可以求出“a比b多的”应为(n+k)/2,“b比a多的”为(n-k)/2,所以答案就是f[n][(n+k)/2],这也是为什么(n-k)必须是偶数的原因
}

你可能感兴趣的:(【BZOJ3622】已经没有什么好害怕的了,DP+容斥原理)