bzoj3622 已经没有什么好害怕的了(容斥原理+DP+二项式反演)

83 篇文章 0 订阅
53 篇文章 0 订阅

bzoj3622 已经没有什么好害怕的了

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=3622

题意:
这里写图片描述

数据范围
1≤ n ≤ 2000,0 ≤ k ≤ n

题解:
首先 k+n k + n 不是偶数就无解了,让 k=(k+n)/2 k = ( k + n ) / 2 ,那么问题就是:
给两组 n n 个数 a1...an a 1 . . . a n b1...bn b 1 . . . b n , 保证数字互不不相同. 问有多少种将它们配对的方式, 使得 ai>bi a i > b i 的对数恰好为k。

如果要算恰好k对的数量,并不好算,但倘若放开范围算>=k的,就有一个比较简单的DP:
a[] a [ ] b[] b [ ] 从小到大排序, t[i] t [ i ] 表示 a a 的第i个比 b b t[i]个大。
定义 f[i][j] f [ i ] [ j ] 表示考虑了 a1...ai a 1 . . . a i , 在其中选出j 个, 且这j 对都满足a > b的方案数。
f[i][j]=f[i1][j]+(t[i]j+1) f [ i ] [ j ] = f [ i − 1 ] [ j ] + ( t [ i ] − j + 1 )
f[n][i](ni)! f [ n ] [ i ] ∗ ( n − i ) ! 就是>=k的方案数,恰好为i的会在其中算 (ik) ( i k ) 次
Fi=f[n][i](ni)! F i = f [ n ] [ i ] ∗ ( n − i ) ! ,真实的恰好有i对的方案数是 gi g i ,有:
Fk=i=kn(ik)gi F k = ∑ i = k n ( i k ) g i
其实已经可以 n2 n 2 推了,但是么根据二项式反演有:
gk=i=kn(1)ik(ik)Fi g k = ∑ i = k n ( − 1 ) i − k ( i k ) F i
就可以直接算了。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int mod=1000000009;
const int N=2005;
int n,k,a[N],b[N],f[N][N],C[N][N],t[N],fac[N];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int j=1;j<=n;j++) scanf("%d",&b[j]);
    if(n<k||(n+k)%2==1) {printf("0\n"); return 0;}
    k=(n+k)/2;
    sort(a+1,a+n+1); sort(b+1,b+n+1);
    fac[0]=1; for(int i=1;i<=n;i++) fac[i]=(1LL*fac[i-1]*i)%mod;
    for(int i=1,j;i<=n;i++) {j=1; while(j<=n&&b[j]<a[i]) j++; t[i]=j-1;}
    for(int i=0;i<=n;i++) 
    for(int j=0;j<=i;j++)
    {
        if(j==0) f[i][j]=1;
        else f[i][j]=(f[i-1][j]+1LL*max(0,t[i]-j+1)*f[i-1][j-1]%mod)%mod;
        if(i==j||j==0) C[i][j]=1;
        else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    }
    int ans=0;
    for(int i=k;i<=n;i++) 
    {
        int w; if((i-k)%2) w=-1;else w=1;
        int ret=(1LL*C[i][k]*fac[n-i])%mod;
        ret=(1LL*ret*f[n][i])%mod;
        ans=ans+ret*w; if(ans<0) ans+=mod;
        ans%=mod;
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值