基础二项式反演 [已经没有什么好害怕的了]

link

大意:

给定两个数组a,b,要求对两个数组中的元素两两配对,使得恰好有ai>bi的组数恰好比ai

数据保证所有元素都不相同

思路:
设ai>bi的组数为x,则有x+x-k=n,推出x=(n+k)/2

所以如果n+k是个奇数的话就直接结束了

我们现在令k=(n+k)/2,那么问题就变成了找使得ai>bi的组数恰好为k的方案数

考虑二项式反演。

我们令fi表示a>b的组数恰好为i的方案数,显然答案就是fk

令gi表示a>b的组数至少为i的方案数

那么只要求出来g数组,我们直接套一个二项式反演就可以了

可以考虑通过dp来求出g数组

我们不妨先对两个数组排一下序,并设ri表示对于ai,b数组的前i个元素里面

设dpi,j表示前i个数字里面至少有j对a>b的配对

转移方程也很好想,如果a数组的第i个数字不选,方案数就是dpi-1,j,如果选上它,我们要为其找一个比它小的数字,那么方案数显然就是dpi-1,j-1*(ri-(j-1))

求出来dp之后,gi=(n-i)!*dp[n][i]

然后套一下二项式反演就可以了

code

#include
using namespace std;
#define ll long long
#define endl '\n'
const ll N=5010;
const ll mod=1e9+9;
ll n,m;
ll p[N];
ll pp[N];
ll g[N];
ll num[N];
ll dp[N][N];
ll a[N],b[N];
ll ksm(ll x,ll y)
{
	ll ans=1;
	while(y)
	{
		if(y&1) ans=ans*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return ans;
}
ll inv(ll x)
{
	return ksm(x,mod-2);
}
void init()
{
	p[0]=1;
	for(ll i=1;i<=5000;++i) p[i]=p[i-1]*i%mod;
	pp[5000]=inv(p[5000]);
	for(ll i=5000-1;i>=0;--i)
	pp[i]=pp[i+1]*(i+1)%mod;
}
ll C(ll n,ll m){
	if(n>n>>m;
	if((n+m)%2)
	{
		cout<<0<>a[i];
	for(int i=1;i<=n;++i) cin>>b[i];
	sort(a+1,a+1+n);
	sort(b+1,b+1+n);
	for(int i=1;i<=n;++i)
	{
		num[i]=lower_bound(b+1,b+1+n,a[i])-b-1;
	}
//	for(int i=1;i<=n;++i) cout<>t;while(t--)
	solve();
	return 0; 
}

你可能感兴趣的:(数学,算法,动态规划)