计算
[sum_isum_{j
e i}{a_i+b_i+a_j+b_jchoose{a_i+a_j}}
]
(nle10^5,sum a_i+sum b_ile2 imes10^7)
感觉思维非常僵化啊,当时一看就以为是BBQ hard的加强版,然后直接写了个复杂度有点爆炸的分治做法,小点做BBQ hard,大点暴力。
然而这题并没有这么复杂,我们因为值域不是很大,所以考虑在值域上做点操作。
考虑 (a_i+b_i+a_j+b_jchoose{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) 个,那么我们可以等价写成下面这个式子:
[egin{aligned}=&sum_{t=0}^{a_i+a_j}{a_i+b_ichoose{t}} imes{a_j+b_jchoose a_i+a_j-t}\=&sum_{t=-a_i}^{a_j}{a_i+b_ichoose a_i+t} imes{a_j+b_jchoose 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;
}