前缀和和差分

一维前缀和

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 
 4 const int N = 100010;
 5 int n,m;
 6 int a[N],s[N]; //初始化s[0] = 0
 7 
 8 int main()
 9 {
10     scanf("%d%d", &n, &m);
11     for (int i = 1; i <= n; i ++ ) cin >> a[i];
12     
13     for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i]; //前缀和读入(公式)初始化
14     
15     while (m -- ) // m次读入
16     {
17         int l,r;
18         scanf("%d%d", &l, &r);
19         printf("%d\n",s[r] - s[l - 1]);//部分前缀和
20     }
21     return 0;
22 }
View Code
复制代码

例题:acwing 1230 k倍数=区间问题

时间换空间,且cnt[0] = 1;

复制代码
 1 #include<bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 typedef long long LL;
 6 
 7 const int N = 100010;
 8 int n,k;
 9 LL A[N];
10 LL s[N],cnt[N];//cnt计算相同余数的序列的个数总和 
11 LL res;//计算不相等的余数的个数总和 
12 
13 int main(){
14     scanf("%d %d",&n,&k);
15 
16 //为什么赋值为1?
17 //因为我们的思路是找两个序列和a%k和b%k的余数相同的个数
18 //而我们的前缀和一般是不包含S0这个东西的,因为没有意义,但是这道题有意义
19 //样例里面前缀和序列%k之后是1 1 0 0 1,两两比较,我们只能找到四个
20 //为什么少了两个?因为我们不一定需要两个序列,单个序列取余=0也构成K倍区间
21 //也就是说s3 s4这两个区间是能单独成K倍区间的,而我们的思路是找两个序列比较
22 //此时,我们就要假设S0=0是有意义的,我们就可以有(s0,s3),(s0,s4)这两个组合
23     cnt[0] = 1;
24     for(int i = 1; i <= n; i ++){
25         scanf("%lld",&A[i]);
26         s[i] = s[i - 1] + A[i];
27         s[i] = s[i] % k; 
28         res += (cnt[s[i]]);
29         cnt[s[i]] ++;//记录序列%k不同余数的个数 
30         //cnt[s[i]]要在res之后做,因为它是记录总数,如果下面再加回去会重复。 
31         /*不能写成
32         cnt[s[i]]++;
33         res+=(cnt[s[i]]);
34         */
35         //用笔写字调试一下就知道了 
36     }
37     //cnt[0] = 1; 
38     /*for(int i = 1; i <= n; i ++){
39         /*for(int j = i; j <= n; j ++){
40             if(s[j] == 0 && j > 0) break;
41             if((s[j] - s[i - 1]) % k == 0){
42                 res ++;
43             }
44         }
45         改成一重循环可以从这里删去。
46         这段代码的含义是: 当j固定时,在1-R之间,
47                         找到有多少个L,满足([S[右端点] - S[左端点])%k == 0
48         从含义上优化代码
49         空间换时间 
50         */
51         //res += (cnt[s[i]]);
52     /*我刚开始迷惑为什么不是当cnt[s[i]]>=2的时候才加, 
53       现在懂了,因为不一定是两个区间的重叠序列构成K倍区间,
54       一个序列本身如果连续和%k=0也构成K倍区间。故所有的都要加上
55     */   
56 
57     /*10^10>10^8 TLE -> 要改为一重循环*/
58     printf("%lld\n",res);
59     return 0;
60 }
View Code
复制代码

 

 

二维前缀和

复制代码
 1  1 #include<iostream>
 2  2 using namespace std;
 3  3 
 4  4 const int N = 1010;
 5  5 int n,m,q;
 6  6 int a[N][N],s[N][N];
 7  7 
 8  8 int main()
 9  9 {
10 10     scanf("%d%d%d", &n, &m,&q);
11 11     for(int i = 1;i <= n; i ++)
12 12        for(int j = 1;j <= m; j ++)
13 13         scanf("%d", &a[i][j]);
14 14         
15 15     for(int i = 1; i <= n; i ++)
16 16        for(int j = 1; j <= m ; j ++)
17 17         s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j]; //求前缀和
18 18     
19 19     while(q -- )
20 20     {
21 21         int x1,y1,x2,y2;
22 22         scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
23 23         printf("%d\n",s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1]);//算子矩阵的合
24 24     }
25 25     return 0;
26 26 }
View Code
复制代码

 

 

        

 

S[i,j]S[i,j]即为图1红框中所有数的的和为:

S[i,j]=S[i,j−1]+S[i−1,j]−S[i−1,j−1]+a[i,j]S[i,j]=S[i,j−1]+S[i−1,j]−S[i−1,j−1]+a[i,j]
(x1,y1),(x2,y2)(x1,y1),(x2,y2)这一子矩阵中的所有数之和为:S[x2,y2]−S[x1−1,y2]−S[x2,y1−1]+S[x1−1,

 

一维差分

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 
 4 const int N = 100010;
 5 int n,m,q;
 6 int a[N],b[N];
 7 
 8 void insert(int l,int r,int c)
 9 {
10     b[l] += c;
11     b[r+ 1 ] -= c;
12 }
13 
14 int main()
15 {
16     scanf("%d%d", &n, &m);
17     for(int i = 1; i <= n; i ++)
18     scanf("%d",&a[i]);
19     
20     for(int i = 1; i <= n; i ++) insert(i,i,a[i]); //假定a数组是空的,用差分导入a数组
21     
22     while (m -- )
23     {
24         int l,r,c;
25         scanf("%d%d%d",&l, &r, &c);
26         insert(l,r,c);
27     }
28     for(int i = 1; i <= n; i ++) b[i] += b[i - 1]; //前缀和求b数组
29     
30     for(int i = 1; i <= n; i ++) printf("%d ",b[i]);
31     return 0;
32 }
复制代码

 

二维差分

复制代码
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int N = 1e3 + 10;
 5 int a[N][N], b[N][N];
 6 void insert(int x1, int y1, int x2, int y2, int c)
 7 {
 8     b[x1][y1] += c;
 9     b[x2 + 1][y1] -= c;
10     b[x1][y2 + 1] -= c;
11     b[x2 + 1][y2 + 1] += c;
12 }
13 int main()
14 {
15     int n, m, q;
16     cin >> n >> m >> q;
17     for (int i = 1; i <= n; i++)
18         for (int j = 1; j <= m; j++)
19             cin >> a[i][j];
20     for (int i = 1; i <= n; i++)
21     {
22         for (int j = 1; j <= m; j++)
23         {
24             insert(i, j, i, j, a[i][j]);      //构建差分数组
25         }
26     }
27     while (q--)
28     {
29         int x1, y1, x2, y2, c;
30         cin >> x1 >> y1 >> x2 >> y2 >> c;
31         insert(x1, y1, x2, y2, c);
32     }
33     for (int i = 1; i <= n; i++)
34     {
35         for (int j = 1; j <= m; j++)
36         {
37             b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];  //二维前缀和
38         }
39     }
40     for (int i = 1; i <= n; i++)
41     {
42         for (int j = 1; j <= m; j++)
43         {
44             printf("%d ", b[i][j]);
45         }
46         printf("\n");
47     }
48     return 0;
49 }
复制代码

 

posted @   rw156  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示