[AGC001E] BBQ Hard


Problem Statement

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, $A_i$ pieces of beef and $B_i$ 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 $10^9+7$.


  • $2≦N≦200,000$
  • $1≦A_i≦2000, 1≦B_i≦2000$


The input is given from Standard Input in the following format:

$A_1$ $B_1$
$A_2$ $B_2$
$A_N$ $B_N$


Print the number of the different ways Snuke can make a serving of Skewer Meal, modulo $10^9+7$.

Sample Input 1

1 1
1 1
2 1

Sample Output 1


题目的意思是,对于所有的不同的 (i,j),要在 (a_i+a_j+b_i+b_j) 中选出 (a_i+a_j) 个位置给牛肉。换言之,求值:

[sumlimits_{i=1}^nsumlimits_{j=i+1}^n C_{a_i+a_j+b_i+b_j}^{a_i+b_i} ]

所以这东西怎么求呢?观察到 (a_i,b_ile 1000),考虑能不能使用这个性质。

接下来是很玄学的一步:把 (C_{a_i+a_j+b_i+b_j}^{a_i+b_i}) 看作从点 ((0,0)) 到达 ((a_i+b_i,a_j+b_j)) 的路径数

但这样 dp 次数有点多,再转化一层,看作从点 ((-a_i,-b_i)) 走到 ((a_j,b_j))

注意到此时两个点已经分别只和 (i)(j) 有关了。再预处理时将所有 (dp_{-a_i,-b_i}) 的值赋值为 1,然后跑完 dp,将所有的 (dp_{a_i,b_i}) 的值加起来,复杂度 (O(max(a_i,b_i)^2))

注意到 ((-a_i,-b_i))((a_i,b_i)) 的值都不计入答案,可以减去,然后每种情况被算了两次,所以要再除以2.

const int N=2e5+5,M=2005,P=1e9+7,iv2=P+1>>1;
int n,a[N],b[N],ans,c[M<<2][M<<2],f[M<<1][M<<1];
int main()
	for(int i=1;i<=n;i++)
	for(int i=-2000;i<=2000;i++)
		for(int j=-2000;j<=2000;j++)
	for(int i=c[0][0]=1;i<M*4;i++)
		for(int j=1;j<i;j++)
//	printf("%d %dn",c[4][2],c[6][4]);
//	printf("%dn",f[M+1][M+1]);
	for(int i=1;i<=n;i++)