题目链接
题目
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;
}