AGC001E BBQ Hard 组合、递推

传送门

题意:给出长度为$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 }
View Code

②$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 }
View Code

转载于:https://www.cnblogs.com/Itst/p/9851317.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值