题解 【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;
}
posted @ 2021-07-14 15:26  DreamsChaser  阅读(53)  评论(0)    收藏  举报
Live2D