AGC001E BBQ Hard

传送门

题意:
有n个背包,第i个背包里有一个编号为i的棍子、Ai个肉和Bi个菜。任选两个不同的背包,把这两个背包里所有的肉和菜都用这两根棍子串起来形成一个烤串,问能串出多少种烤串。
当且仅当至少有一根棍子的编号不同,或者是肉和菜的数目不同或者是排列方式不同时,称这两种烤串是不同的。


一个比较显然的暴力:枚举2个背包,然后求出 1 i < j n C a i + a j + b i + b j a i + a j \displaystyle\sum_{1\le i<j\le n} C^{a_i+a_j}_{a_i+a_j+b_i+b_j} ,但对于这种数据范围显然不够。

那我们考虑一下这个式子有什么几何意义。
好像是从 ( a i , b i ) (-a_i,-b_i) ( a j , b j ) (a_j,b_j) 的方案数?
说明:从 ( a i , b i ) (-a_i,-b_i) ( a j , b j ) (a_j,b_j) 需要 a i + a j + b i + b j a_i+a_j+b_i+b_j 步,其中有 a i + a j a_i+a_j 步往水平方向走。

那么就可以dp了,dp[i][j]为从左下角到(i,j)的方案数,cnt[i][j]为落在(i,j)上的点的个数。
显然是从左边和下面转移过来的,所以不多说。
最后答案就成了 0 i , j 2000 d p [ i ] [ j ] c n t [ i ] [ j ] \displaystyle\sum_{0\le i, j\le 2000} dp[i][j]*cnt[i][j]
然后发现多算了自己到自己的情况,要减掉。
最后结果除以2.(我也不清楚为什么…反正某题解是这样做的QAQ有哪位dalao可以解释吗)

被注释掉的就是暴力代码。

#include<cstdio>
#define mod 1000000007
#define maxn 200005
#define N 2000
int n,fact[N*4+5],inv[N*4+5],ans,cnt[N*2+5][N*2+5],dp[N*2+5][N*2+5],inv2;
void init()
{
	fact[0]=1;
	for(int i=1;i<=N*4;i++) fact[i]=1ll*fact[i-1]*i%mod;
	inv[1]=1;
	for(int i=2;i<=N*4;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	inv[0]=1,inv2=inv[2];
	for(int i=1;i<=N*4;i++) inv[i]=1ll*inv[i-1]*inv[i]%mod;
}
int F(int a,int b) { return 1ll*fact[a+b]*inv[a]%mod*inv[b]%mod; }
int main()
{
	scanf("%d",&n);
	init();
	for(int i=1;i<=n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		cnt[N+a][N+b]++,dp[N-a][N-b]++;
		ans=(ans+mod-F(a*2,b*2))%mod;
	}
	for(int i=0;i<=N*2;i++)
		for(int j=0;j<=N*2;j++)
		{
			if(i) dp[i][j]+=dp[i-1][j];
			if(j) dp[i][j]+=dp[i][j-1];
			dp[i][j]%=mod;
			ans=(ans+1ll*dp[i][j]*cnt[i][j]%mod)%mod;
		}
	/*for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			ans=(ans+F(a[i]+a[j],b[i]+b[j]))%mod;*/
	printf("%lld\n",1ll*ans*inv2%mod);
}

猜你喜欢

转载自blog.csdn.net/dogeding/article/details/82765665