BZOJ 3622 已经没有什么好害怕的了

本文介绍了一种结合动态规划、组合数学及容斥原理解决特定问题的方法。通过排序和预处理,利用f[i][j]表示状态转移方程,最终通过容斥原理计算结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

Input

Output

Sample Input

4 2
5 35 15 45
40 20 10 30

Sample Output

4

HINT


输入的2*n个数字保证全不相同。


还有输入应该是第二行是糖果,第三行是药片

Source

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DP+组合数+容斥原理~

先将a和c从小到大排序,预处理出g[i]表示使得a[i]>c[j]成立的最大j。

用f[i][j]表示目前到第i个糖,至少有j对糖>药的方案数,那么f[i][j]=f[i-1][j]+f[i-1][j-1]*(g[i]-j+1)。

然后我们用容斥原理,枚举剩余的对中还有多少种满足条件,得f[n][i]=f[n][i]*(n-i)!-sum{f[n][j]*c(j,i)}。

注意输入的k不是我写的k,要自己求得;如果(n-k)&1==1的话,答案是0。


#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long

const int mod=1e9+9;

int n,K,a[2001],c[2001],g[2001],f[2001][2001],k[2001][2001],sheng[2001],jiang[2001];

int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

int mi(int u,int v)
{
	int now=1;
	for(;v;v>>=1,u=(ll)u*u%mod) if(v&1) now=(ll)now*u%mod;
	return now;
}

void init()
{
	sheng[0]=jiang[0]=1;
	for(int i=1;i<=n;i++) sheng[i]=(ll)sheng[i-1]*i%mod;
	jiang[n]=mi(sheng[n],mod-2);
	for(int i=n-1;i;i--) jiang[i]=(ll)jiang[i+1]*(i+1)%mod;
}

int C(int n,int m)
{
	return (ll)sheng[n]*jiang[m]%mod*jiang[n-m]%mod;
}

void add(int &a,int b)
{
	a=a+b>=mod ? a+b-mod:a+b;
}

void sub(int &a,int b)
{
	a=a>=b ? a-b:a-b+mod;
}

int main()
{
	n=read();K=read();
	if((n-K)&1)
	{
		puts("0");return 0;
	}
	K=(n+K)>>1;init();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) c[i]=read();
	sort(a+1,a+n+1);sort(c+1,c+n+1);
	for(int i=1,j=1;i<=n;i++)
	{
		for(;a[i]>c[j] && j<=n;j++);
		g[i]=--j;
	}
	f[0][0]=1;
	for(int i=1;i<=n;i++)
	  for(int j=0;j<=i;j++)
	  {
	  	f[i][j]=f[i-1][j];
	  	if(j && g[i]-j+1>0) add(f[i][j],(ll)f[i-1][j-1]*(g[i]-j+1)%mod);
	  }
	for(int i=n;i>=K;i--)
	{
		f[n][i]=(ll)f[n][i]*sheng[n-i]%mod;
		for(int j=i+1;j<=n;j++) sub(f[n][i],(ll)f[n][j]*C(j,i)%mod);
	}
	printf("%d\n",f[n][K]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值