AGC001 E - BBQ Hard【dp+组合数学】

首先直接按要求列出式子是\( \sum_{i=1}{n}\sum_{j=i+1}C_{a_i+a_j+b_i+b_j}^{a_i+a_j} \)
这样显然过不了,因为ab的数据范围比较小,所以从这里入手,注意到后面的组合数含义是从点(ai,bi)走到点(-aj,-bj)的方案数
把但是j从i+1开始不好做,就容斥一下,把式子变成\( \sum_{i=1}{n}\sum_{j=1}C_{a_i+a_j+b_i+b_j}{a_i+a_j}-\sum_{i=1}C_{a_i+a_i+b_i+b_i}^{a_i+a_j} \)
所以设f[i][j]为从点(i,j)左下方存在的点走到点(i,j)的方案数总和,转移是f[i][j]+=f[i-1][j]+f[i][j-1]也就是从左点和下点走一步过来再加上这个点本来就存在的,就是原来的(ai,bi)要变成(-ai,-bi)
然后dp完对每个点都加到答案里再减掉后面的部分即可

#include<iostream>
#include<cstdio>
using namespace std;
const int N=200005,mod=1e9+7;
int n,a[N],b[N],f[4005][4005],fac[N],inv[N],ans;
int read()
{
	int r=0,f=1;
	char p=getchar();
	while(p>'9'||p<'0')
	{
		if(p=='-')
			f=-1;
		p=getchar();
	}
	while(p>='0'&&p<='9')
	{
		r=r*10+p-48;
		p=getchar();
	}
	return r*f;
}
int ksm(int a,int b)
{
	int r=1;
	while(b)
	{
		if(b&1)
			r=1ll*r*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return r;
}
int C(int n,int m)
{
	return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
	fac[0]=inv[0]=1;
	for(int i=1;i<=10000;i++)
		fac[i]=1ll*fac[i-1]*i%mod;
	inv[10000]=ksm(fac[10000],mod-2);
	for(int i=9999;i>=1;i--)
		inv[i]=1ll*inv[i+1]*(i+1)%mod;
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read(),b[i]=read();
		f[2001-a[i]][2001-b[i]]++;
		ans=(ans-C(a[i]*2+b[i]*2,a[i]*2))%mod;
	}
	for(int i=1;i<=4002;i++)
		for(int j=1;j<=4002;j++)
			f[i][j]=(f[i][j]+f[i-1][j]+f[i][j-1])%mod;
	for(int i=1;i<=n;i++)
		ans=(ans+f[2001+a[i]][2001+b[i]])%mod;
	printf("%d\n",(1ll*ans*ksm(2,mod-2)%mod+mod)%mod);
	return 0;
}
posted @ 2019-05-25 16:16  lokiii  阅读(131)  评论(0编辑  收藏  举报