[2020多校A层12.1] 礼物

计算

\[\sum_i\sum_{j\ne i}{a_i+b_i+a_j+b_j\choose{a_i+a_j}} \]

\(n\le10^5,\sum a_i+\sum b_i\le2\times10^7\)


感觉思维非常僵化啊,当时一看就以为是BBQ hard的加强版,然后直接写了个复杂度有点爆炸的分治做法,小点做BBQ hard,大点暴力。

然而这题并没有这么复杂,我们因为值域不是很大,所以考虑在值域上做点操作。

考虑 \(a_i+b_i+a_j+b_j\choose{a_i+a_j}\) 这个式子的组合意义,其实是从 \(a_i+b_i+a_j+b_j\) 个物品里选 \(a_i+a_j\) 个,那么我们考虑分两次选这个物品,第一次从 \(a_i+b_i\) 里选 \(t\) 个,那么第二次就是从 \(a_j+b_j\) 里选 \(a_i+a_j-t\) 个,那么我们可以等价写成下面这个式子:

\[\begin{aligned}=&\sum_{t=0}^{a_i+a_j}{a_i+b_i\choose{t}}\times{a_j+b_j\choose a_i+a_j-t}\\=&\sum_{t=-a_i}^{a_j}{a_i+b_i\choose a_i+t}\times{a_j+b_j\choose a_j-t}\end{aligned} \]

\(t\) 如果很大左边的组合数就是 \(0\) ,那么 \(t\) 的上界变成 \(b_i\) ,如果 \(b_i>a_j\) ,那么右边的组合数就是 \(0\),所以不会多算。

于是两边可以分别算和再求乘积,那么对于右边那一项可以开个桶记录和,这样就可以在 \(O(\sum a_i+\sum b_i)\) 的复杂度做完了。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 1e5;
const int MAXN = 2e7;
const int p = 1e9 + 7;
using namespace std;
struct thi
{
    int a,b;
}c[N + 5],d[N + 5];
int n,a[N + 5],b[N + 5],ans,inv[MAXN + 1],fac[MAXN + 1],sm[MAXN * 2 + 5];
int mypow(int a,int x){int s = 1;for (;x;x & 1 ? s = 1ll * s * a % p : 0,a = 1ll * a * a % p,x >>= 1);return s;}
int C(int n,int m)
{
    return 1ll * fac[n] * inv[m] % p * inv[n - m] % p;
}
int main()
{
    //freopen("gift.in","r",stdin);
    //freopen("gift.out","w",stdout);
    scanf("%d",&n);
    for( int i = 1;i <= n;i++)
        scanf("%d%d",&a[i],&b[i]);
    fac[0] = 1;
    for (int i = 1;i <= MAXN;i++)
        fac[i] = 1ll * fac[i - 1] * i % p;
    inv[MAXN] = mypow(fac[MAXN],p - 2);
    for (int i = MAXN - 1;i >= 0;i--)
        inv[i] = 1ll * inv[i + 1] * (i + 1) % p;
    for (int i = 1;i <= n;i++)
    {
        for (int t = -a[i];t <= b[i];t++)
            ans += 1ll * C(a[i] + b[i],t + a[i]) * sm[-t + MAXN] % p,ans %= p;
        for (int t = -a[i];t <= b[i];t++)
            sm[t + MAXN] += C(a[i] + b[i],a[i] + t) % p,sm[t + MAXN] %= p;
    }
    cout<<(2ll * ans % p + p) % p<<endl;
    return 0;
}
posted @ 2020-12-01 15:14  eee_hoho  阅读(94)  评论(0编辑  收藏  举报