题意:给出长度为$N$的两个正整数序列$A_i,B_i$,求$\sum\limits_{i=1}^N \sum\limits_{j=i+1}^N C_{A_i+A_j+B_i+B_j}^{A_i+B_i}$。$N \leq 2 \times 10^5$
给出两种数据范围以及对应做法:
①$1 \leq A_i,B_i \leq 2000$(原题数据范围)
我们可以发现$C_{A_i+A_j+B_i+B_j}^{A_i+B_i}$就相当于在平面直角坐标系上限定每一次只能向上或者向右走一个单位的情况下,从$(-A_i,-B_i)$走到$(A_j,B_j)$的方案数量,所以原来的题目就可以抽象为一些第三象限的点限定每一次只能向上或者向右走一个单位的情况下走到第一象限中的任意一个点的方案数的和。我们可以将所有$(-A_i,-B_i)$的权值设为$1$,然后递推得到每一个$(A_j,B_j)$的方案数即可。注意:要将$(-A_i,B_i)$到$(A_i,B_i)$的路径方案数减掉(直接组合数即可),同时因为一个数对会被算两边,所以最后要乘上$2$的逆元。总时间复杂度为$O(N+\text{值域}^2)$
1 #include<bits/stdc++.h>
2 using namespace std;
3
4 inline int read(){
5 int a = 0;
6 bool f = 0;
7 char c = getchar();
8 while(c != EOF && !isdigit(c)){
9 if(c == '-')
10 f = 1;
11 c = getchar();
12 }
13 while(c != EOF && isdigit(c)){
14 a = (a << 3) + (a << 1) + (c ^ '0');
15 c = getchar();
16 }
17 return f ? -a : a;
18 }
19
20 const int MOD = 1e9 + 7 , MAXN = 4000 , MAXM = 200000;
21 int f[MAXN + 10][MAXN + 10] , jc[MAXN + 10 << 1] = {1 , 1} , ny[MAXN + 10 << 1] = {1} , a[MAXM + 10] , b[MAXM + 10];
22
23 inline long long poww(long long a , int b){
24 int times = 1;
25 while(b){
26 if(b & 1)
27 times = times * a % MOD;
28 a = a * a % MOD;
29 b >>= 1;
30 }
31 return times;
32 }
33
34 int main(){
35 for(long long i = 2 ; i <= MAXN << 1 ; i++)
36 jc[i] = jc[i - 1] * i % MOD;
37 ny[MAXN << 1] = poww(jc[MAXN << 1] , MOD - 2);
38 for(long long i = (MAXN << 1) - 1 ; i ; i--)
39 ny[i] = ny[i + 1] * (i + 1) % MOD;
40 int N = read() , ans = 0;
41 for(int i = 1 ; i <= N ; i++){
42 a[i] = read();
43 b[i] = read();
44 f[-a[i] + 2000][-b[i] + 2000]++;
45 ans = (ans + MOD - jc[a[i] + b[i] << 1] * (long long)ny[a[i] << 1] % MOD * ny[b[i] << 1] % MOD) % MOD;
46 }
47 for(int i = 1 ; i <= MAXN ; i++){
48 f[i][0] += f[i - 1][0];
49 f[0][i] += f[0][i - 1];
50 }
51 for(int i = 1 ; i <= MAXN ; i++)
52 for(int j = 1 ; j <= MAXN ; j++)
53 f[i][j] = ((f[i][j] + f[i - 1][j]) % MOD + f[i][j - 1]) % MOD;
54 for(int i = 1 ; i <= N ; i++)
55 ans = (ans + f[a[i] + 2000][b[i] + 2000]) % MOD;
56 cout << ans * poww(2 , MOD - 2) % MOD;
57 return 0;
58 }
②$1 \leq A_i,B_i \leq 10^6 , \sum(A_i + B_i) \leq 2 \times 10^7$
注意到后面给的和的条件,思考如何去利用它。
将$C_{A_i+A_j+B_i+B_j}^{A_i+B_i}$稍微变形:
$C_{A_i+A_j+B_i+B_j}^{A_i+B_i}$
$= \sum\limits_{k=0}^{A_i+A_j} C_{A_i+B_i}^k \times C_{A_j+B_j}^{A_i+A_j-k}$
$= \sum\limits_{k=-A_i}^{A_j} C_{A_i+B_i}^{A_i+k} \times C_{A_j+B_j}^{A_j-k}$
$= \sum\limits_{k=-A_i}^{B_i} C_{A_i+B_i}^{A_i+k} \times C_{A_j+B_j}^{A_j-k}$
(至于最后一步,因为$k>B_i$时,$C_{A_i+B_i}^{A_i+k}=0$,而$k>A_j$时$C_{A_j+B_j}^{A_j-k}=0$,所以$A_j$与$B_i$是等价的)
所以我们维护$f_i$表示在计算$now$的答案时$\sum\limits_{j=1}^{now-1}C_{A_j+B_j}^{A_j-i}$的值,然后每一次通过$f$数组得到当前答案,并用当前的状态更新$f$数组即可。
复杂度$O(\sum(A_i + B_i) + N)$
1 #include<bits/stdc++.h>
2 using namespace std;
3
4 inline int read(){
5 int a = 0;
6 bool f = 0;
7 char c = getchar();
8 while(c != EOF && !isdigit(c)){
9 if(c == '-')
10 f = 1;
11 c = getchar();
12 }
13 while(c != EOF && isdigit(c)){
14 a = (a << 3) + (a << 1) + (c ^ '0');
15 c = getchar();
16 }
17 return f ? -a : a;
18 }
19
20 const int MOD = 1e9 + 7 , MAXN = 4000;
21 int f[MAXN + 10 << 1] , jc[MAXN + 10] = {1 , 1} , ny[MAXN + 10] = {1};
22
23 inline long long poww(long long a , int b){
24 int times = 1;
25 while(b){
26 if(b & 1)
27 times = times * a % MOD;
28 a = a * a % MOD;
29 b >>= 1;
30 }
31 return times;
32 }
33
34 int main(){
35 for(long long i = 2 ; i <= MAXN ; i++)
36 jc[i] = jc[i - 1] * i % MOD;
37 ny[MAXN] = poww(jc[MAXN] , MOD - 2);
38 for(long long i = MAXN - 1 ; i ; i--)
39 ny[i] = ny[i + 1] * (i + 1) % MOD;
40 int N = read() , ans = 0;
41 for(int i = 1 ; i <= N ; i++){
42 long long a = read() , b = read();
43 for(int j = -a ; j <= b ; j++)
44 ans = (ans + f[MAXN + j] * (long long)jc[a + b] % MOD * ny[a + j] % MOD * ny[b - j]) % MOD;
45 for(int j = -b ; j <= a ; j++)
46 f[MAXN + j] = (f[MAXN + j] + (long long)jc[a + b] * ny[a - j] % MOD * ny[b + j]) % MOD;
47 }
48 cout << ans % MOD;
49 return 0;
50 }