[题解] [AGC 001 E] BBQ Hard

题面

题解

先化简一下

[displaystyle egin{aligned}&sum_{i = 1}^{n}sum_{j = i + 1}^{n}inom{a_i+a_j+b_i+b_j}{a_i+a_j}\=&frac{sum_{i = 1}^{n}sum_{j = 1}^{n}inom{a_i+a_j+b_i+b_j}{a_i+a_j}-sum_{i = 1}^{n}inom{2*a_i+2*b_i}{2*a_i}}{2}end{aligned} ]

后面那个可以 (O(n)) 地算出来

所以现在的问题就是前面这个东西怎么算

仔细观察一下括号内的东西, 发现可以把他变成网格图计数

就是 ((0, 0))((a_i + a_j, b_i + b_j)) 的路径数

再换一下, 变成从 ((-a_i, -b_i))((a_j, b_j)) 的方案数

考虑到 (a)(b) 都很小, 你网格图跑一遍路径和就完了

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
const int mod = 1000000007;
const int N = 4005;
const int M = 8000; 
using namespace std;
 
int f[N + 505][N + 505], a[200005], b[200005], fac[M << 1], inv[M << 1], base, ans, n; 
 
template < typename T >
inline T read()
{
	T x = 0, w = 1; char c = getchar();
	while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * w; 
}
 
int fpow(int x, int y)
{
	int res = 1;
	while(y)
	{
		if(y & 1)
			res = 1ll * res * x % mod;
		x = 1ll * x * x % mod;
		y >>= 1; 
	}
	return res; 
}
 
int C(int n, int m) { return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod; }
 
int main()
{
	n = read <int> (), base = 2002;
	for(int i = (fac[0] = 1); i <= M; i++)
		fac[i] = 1ll * fac[i - 1] * i % mod;
	inv[M] = fpow(fac[M], mod - 2);
	for(int i = M - 1; i >= 0; i--)
		inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
	for(int i = 1; i <= n; i++)
	{
		a[i] = read <int> (), b[i] = read <int> ();
		f[base - a[i]][base - b[i]]++; 
	}
	for(int i = 1; i <= base + 2000; i++)
		for(int j = 1; j <= base + 2000; j++)
			f[i][j] = (1ll * f[i][j] + f[i - 1][j] + f[i][j - 1]) % mod;
	for(int i = 1; i <= n; i++)
	{
		ans = (1ll * ans + f[base + a[i]][base + b[i]]) % mod;
		ans = (1ll * ans - C(2 * a[i] + 2 * b[i], 2 * a[i])) % mod; 
	}
	ans = (ans + mod) % mod;
	ans = 1ll * ans * 500000004 % mod;
	printf("%d
", ans); 
	return 0; 
}
原文地址:https://www.cnblogs.com/ztlztl/p/12187834.html