1. 程式人生 > 其它 >【AGC001E E - BBQ Hard】 題解

【AGC001E E - BBQ Hard】 題解

題目連結

題目

Snuke is having another barbeque party.

This time, he will make one serving of Skewer Meal.

He has a stock of
N Skewer Meal Packs. The i-th Skewer Meal Pack contains one skewer, Ai​ pieces of beef and

Bi​ pieces of green pepper. All skewers in these packs are different and distinguishable, while all pieces of beef and all pieces of green pepper are, respectively, indistinguishable.

To make a Skewer Meal, he chooses two of his Skewer Meal Packs, and takes out all of the contents from the chosen packs, that is, two skewers and some pieces of beef or green pepper. (Remaining Skewer Meal Packs will not be used.) Then, all those pieces of food are threaded onto both skewers, one by one, in any order.

(See the image in the Sample section for better understanding.)

In how many different ways can he make a Skewer Meal? Two ways of making a Skewer Meal is different if and only if the sets of the used skewers are different, or the orders of the pieces of food are different. Since this number can be extremely large, find it modulo
109+7.

思路

題目本質上就是在求:

\[\Large\sum_{i=1}^{n}\sum_{j=i + 1}^{n}{a_i+b_i+a_j+b_j \choose a_i+a_j} \]

然而 \(n\) 很大,\(a_i\)\(b_i\) 的範圍確很小。這給了我們提示。

讓我們觀察式子 \(\Large a_i+b_i+a_j+b_j \choose \Large a_i+a_j\)

這不就是在求從 \((0,0)\) 走到 \((a_i+a_j, b_i+b_j)\) 的方案數嗎?

可是這樣子不好求,於是我們也可以把他理解為 \((-a_i, -b_i)\) 走到 \((a_j,b_j)\) 的方案數。(就是相當於 \(x\) 軸全部減 \(a_i\)\(y\) 軸全部減 \(b_i\),而這個矩形的長寬不變)

於是我們先對網格圖中所有 \((-a_i, -b_i)\) 的地方 \(+1\),然後轉化成經典走格子問題,最後統計所有 \((a_j,b_j)\) 的答案。

要注意的是,我們還有減去 \(i=j\) 的情況。並且由於 \(j\)\(i+1\) 開始,所有對於結果我們還有除2。

Code

// Problem: AT1983 [AGC001E] BBQ Hard
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/AT1983
// Memory Limit: 250 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline 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<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define M 4010
#define mo 1000000007
#define N 200010
int n, m, i, j, k; 
int dp[M+10][M+10], ans; 
int a[N], b[N], jc[N]; 

int kuai(int a, int b)
{
	int ans=1; 
	while(b)
	{
		if(b&1) ans=(ans*a)%mo; 
		b>>=1; 
		a=(a*a)%mo; 
	}
	return ans; 
}

int C(int m, int n)
{
	if(m<n) return 0; 
	return jc[m]*kuai(jc[n]*jc[m-n]%mo, mo-2)%mo; 
}

int suan(int lx, int ly, int rx, int ry)
{
	int lenx=rx-lx; 
	int leny=ry-ly; 
	// printf("C(%lld, %lld)=%lld\n", lenx, leny, C(lenx, leny)); 
	return C(lenx+leny, lenx); 
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	for(i=jc[0]=1; i<=N; ++i) jc[i]=jc[i-1]*i%mo; 
	n=read(); 
	for(i=1; i<=n; ++i)
		a[i]=read(), b[i]=read(), dp[2003-a[i]][2003-b[i]]++; 
	for(i=1; i<=M; ++i)
		for(j=1; j<=M; ++j)
			dp[i][j]=(dp[i][j]+dp[i-1][j]+dp[i][j-1])%mo; 
	for(i=1; i<=n; ++i)
	{
		ans=((ans+dp[a[i]+2003][b[i]+2003]
		-suan(2003-a[i], 2003-b[i], a[i]+2003, b[i]+2003))%mo+mo)%mo; 
		// printf("%lld\n", dp[a[i]+2003][b[i]+2003]); 
	}
		
	printf("%lld", ((ans*kuai(2, mo-2)%mo)+mo)%mo); 
	return 0; 
}