洛谷P4859 题解

这篇博客详细解析了洛谷P4859题目,主要涉及二项式反演的经典算法。通过定义dpi,j表示在特定条件下满足条件的方案数,利用递推关系式dpi,j=dpi−1,j+dpi−1,j−1×(bigi−j+1)进行求解。进一步,博主介绍了钦定和恰好满足条件的方案数fi和gi的计算,并给出了二项式反演公式gi=∑i=mn(−1)i−mCim×dpn,i×(n−i)!。最后,博主指出算法的时间复杂度为O(n^2)。" 84261197,7501675,Linux 4G虚拟地址空间详解,"['操作系统', '内存管理', 'Linux内核', '程序设计']

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

  • 二项式反演经典题

我们先设 dpi,jdp_{i,j}dpi,j 表示到 iii 使得 a>ba>ba>b 的个数为 jjj 的方案数。

显然 dpi,j=dpi−1,j+dpi−1,j−1×(bigi−j+1)dp_{i,j}= dp_{i-1,j}+dp_{i-1,j-1}\times (big_i-j+1)dpi,j=dpi1,j+dpi1,j1×(bigij+1),其中 bigibig_ibigi 为在 bbb 中比 aia_iai 的个数。

我们在按套路设 fif_ifi 表示钦定(即只要达到目标后可以继续随便填)a>ba>ba>b 个数为 iii 的方案数。gig_igi 表示恰好a>ba>ba>b 个数为 iii 的方案数。

其中 fm=dpn,m×(n−m)!f_m=dp_{n,m}\times (n-m)!fm=dpn,m×(nm)!

考虑二项式反演,gi=∑i=mn(−1)i−mCimf(i)g_i= \sum_{i=m}^{n} (-1)^{i-m}C_{i}^{m}f(i)gi=i=mn(1)imCimf(i)

最后得:gi=∑i=mn(−1)i−mCim×dpn,i×(n−i)!g_i= \sum_{i=m}^{n} (-1)^{i-m}C_{i}^{m}\times dp_{n,i}\times(n-i)!gi=i=mn(1)imCim×dpn,i×(ni)!

时间复杂度:O(n2)O(n^2)O(n2)

  • CodeCodeCode
#include <bits/stdc++.h>
#define For(i,a,b) for ( int i=(a);i<=(b);i++ )
#define Dow(i,b,a) for ( int i=(b);i>=(a);i-- )
#define GO(i,x) for ( int i=head[x];i;i=e[i].nex )
#define mem(x,s) memset(x,s,sizeof(x))
#define cpy(x,s) memcpy(x,s,sizeof(x))
#define YES return puts("YES"),0
#define NO return puts("NO"),0
#define GG return puts("0"),0
#define pb push_back
#define lowbit(x) x&(-x)
#define int long long
using namespace std;

inline int read()
{
	int sum=0,ff=1; char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-') ff=-1;
		ch=getchar();
	}
	while(isdigit(ch))
		sum=sum*10+(ch^48),ch=getchar();
	return sum*ff;
}

const int mod=1e9+7;
const int mo=1e9+9;
const int N=2e3+5;

inline int min(int x,int y) {return (x>y)?y:x;}
inline int max(int x,int y) {return (x<y)?y:x;}

inline void Add(int &x,int y)
{
	if(y>=mo) y%=mo;
	x+=y;
	if(x>=mo) x-=mo;
}

int n,m,a[N],b[N],C[N][N],fac[N],ans;
int dp[N][N],f[N],g[N];
 
signed main()
{
	n=read(),m=read();
	if((n-m)&1) GG;
	m=(n+m)>>1ll;
	For(i,1,n) a[i]=read();
	For(i,1,n) b[i]=read();
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	C[0][0]=1,fac[0]=1;
	For(i,0,n) dp[i][0]=1;
	int p=0; 
	For(i,1,n)
	{
		while(p<n&&b[p+1]<a[i]) p++;
		For(j,1,n) Add(dp[i][j],dp[i-1][j]+dp[i-1][j-1]*(p-j+1)%mo);
	}
	For(i,1,n)
	{
		C[i][0]=1;
		fac[i]=fac[i-1]*i%mo;
		For(j,1,i) Add(C[i][j],C[i-1][j-1]+C[i-1][j]);
	}
	int ans=0;
	For(i,m,n) 
	{
		if((i-m)&1ll) Add(ans,-1*C[i][m]*fac[n-i]%mo*dp[n][i]%mo+mo);
		else Add(ans,C[i][m]*fac[n-i]%mo*dp[n][i]%mo);
	}
	printf("%lld\n",(ans+mo)%mo);
	return 0;
}
	

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值