1. 程式人生 > 其它 >題解 【AT1983 [AGC001E] BBQ Hard】

題解 【AT1983 [AGC001E] BBQ Hard】

\(\large\mathcal{Description}\)

\(n\) 個數對 \((A_i,A_j)\). 求:

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

答案對 \(10^9+7\) 取模.

\(\large\mathcal{Solution}\)

暴力求解上述式子是 \(\mathcal{O}(n^2)\) 的,我們考慮如何優化它。

給出引理:在平面直角座標系中,點 \((x_1, y_1)\) 為起點, \((x_2,y_2)\) 為終點,每次只能往右或往上走(\(x_2\ge x_1, y_2\ge y_1\)

)。則方案數為:

\[y_2-x_2+y_1-x_1 \choose y_2-x_2 \]

感性理解:從 \((x_1, y_1)\)\((x_2,y_2)\) 要走 \(y_2-x_2+y_1-x_1\) 步,其中 \(y_2-x_2\) 步向上走。

由上述引理,得知組合數的幾何意義: \(a_i+b_i+a_j+b_j \choose a_i+a_j\) 為從 \((-a_i, -b_i)\)\((a_j, b_j)\) 的方案數。發現:起點只與 \(i\) 有關, 終點只與 \(j\) 有關。

\(f(i,\ j)\) 表示 \((i, j)\) 到所有 \((-a_x, -b_x)_{1\le x\le n}\)

路徑條數之和。

\(f(i,\ j)\) 是容易用 \(\text{dp}\) 處理的,方法如下:

  • 對於 \(\forall x\in\{1,2,\cdots,n\}\),令 \(f_{a_i,\ b_i} \leftarrow 1\)
  • 對所有格子執行 \(f_{i, j} = f_{i, j - 1} + f_{i - 1, j}\).

答案自然就是 \(\sum\limits_{i=1}^nf(a_i,\ b_i)\) 再減去每個 \((a_i,b_i)\)\((-a_i,-b_i)\) 路徑條數之和。

即:

\[\sum\limits_{i=1}^nf(a_i,\ b_i)-\sum\limits_{i=1}^n {2a_i+2b_i \choose 2a_i} \]

時間複雜度:\(\mathcal{O}(\max(a_i^2,\ b_i^2)\ +\ n)\)

\(\large\mathcal{Code}\)

#include <bits/stdc++.h>
#define reg register

using namespace std;

const int N = 200010, S = 2010;
const int mod = 1000000007;

int n, a[N], b[N];
int frac[N], inv[N], ifrac[N];
int f[2 * S + 10][2 * S + 10];

void pre()
{
	frac[0] = inv[1] = ifrac[0] = 1;
	for (reg int i = 1; i <= 4 * S; ++ i) frac[i] = 1ll * i * frac[i - 1] % mod;
	for (reg int i = 2; i <= 4 * S; ++ i) inv[i] = (mod - 1ll * (mod / i) * inv[mod % i] % mod) % mod;
	for (reg int i = 1; i <= 4 * S; ++ i) ifrac[i] = 1ll * inv[i] * ifrac[i - 1] % mod;
}

int C(int n, int m)
{
	return 1ll * frac[n] * ifrac[m] % mod * ifrac[n - m] % mod;
}

int main()
{
	scanf("%d", &n);
	for (reg int i = 1; i <= n; ++ i) scanf("%d %d", &a[i], &b[i]);
	
	pre();
	
	for (reg int i = 1; i <= n; ++ i) f[S - a[i]][S - b[i]] ++ ;
	for (reg int i = 1 - S; i <= S; ++ i)
	    for (reg int j = 1 - S; j <= S; ++ j)
	        f[i + S][j + S] = ((f[i + S][j + S] + f[i + S - 1][j + S]) % mod + f[i + S][j + S - 1]) % mod;
	
	int ans = 0;
	for (reg int i = 1; i <= n; ++ i)
	{
		ans = (ans + f[S + a[i]][S + b[i]]) % mod;
		ans = (ans - C(2 * a[i] + 2 * b[i], 2 * a[i]) + mod) % mod;
	}
	printf("%d\n", 1ll * ans * inv[2] % mod);
	
	return 0;
}