关于前缀和和差分的理解应用

发布时间 2023-08-13 01:25:25作者: 江木匠

  前缀和和差分是互相正逆运用的产物。2023-08-13 00:30:28

  1.一维前缀和

   令 a 数组 b[i] 代表 b[1]+b[2]+b[3]+…+b[i]

   Q:问 b[l] 到 b[r] 的和

   A:   O(n),核心步骤:

   在读取b每步都记录 a[i] = b[i]+a[i-1],最后只要输出 a[r+1] - a[l]

   以下为代码

   

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 int a[100005],b[100005],n,m,l,r;
 5 int main()
 6 {
 7     cin>>n>>m;
 8     for(int i = 1 ; i <= n ; i++)
 9     {
10         cin>>b[i];
11         a[i]=a[i-1]+b[i];
12     }
13     while(m--)
14     {
15         cin>>l>>r;
16         cout<<a[r]-a[l-1]<<"\n";
17     }
18     return 0;
19 }

 

   2.二维前缀和

   令 a 数组 例如a[2][3] 代表 b[1][1]+b[2][1]+b[1][2]+b[2][2]+b[1][3]+b[2][3];

   Q:问 b[x1][y1] 到 b[x2][y2] 的和

   A :   O(NM),核心步骤:

   a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + b[i][j]   最后 ans = a[x2][y2] - a[x2][y1-1]-a[x1-1][y2] + a[x1-1][y1-1]

   

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int a[1005][1005],s[1005][1005],n,m,xo,yo,xs,ys,q;
int main()
{
    cin>>n>>m>>q;
    for(int i = 1 ; i <= n ; i++ )
        for(int j = 1 ; j <= m ; j++)
        {
            cin>>a[i][j];
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
        }
    while(q--) {
       cin>>xo>>yo>>xs>>ys;
       ll ans = s[ys][xs]-s[yo-1][xs]-s[yo-1][ys]+s[yo-1][xo-1];
       cout<<ans<<"\n";
    }
    return 0;
}

 

 

   3.一维差分

   令 a 数组 a[i] 代表 b[1]+b[2]+b[3]+…+b[i] 

 

   Q: 如果我们想令从 a[l] 到 a[r] 的 值全部加上c(常数);

   A:    暴力循环只能是TLE,但是通过核心步骤:

    b[i]+=c , b[r+1] - = c;

  实现O(1)解决

 

   以下是代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 ll a[10005],b[10005],l,r,c,n,q;
 5 void insert(int l , int r , int c)
 6 {
 7     b[l]+=c;
 8     b[r+1]-=c;
 9 }
10 int main()
11 {
12     cin>>n>>q;
13     for(int i = 1 ; i <= n ; i++)
14     {
15         cin>>a[i];
16         insert(i,i,a[i]);
17     }
18     while(q--)
19     {
20         cin>>l>>r>>c;
21         insert(l,r,c);
22     }
23     //前缀和操作
24     for(int i = 1 ; i <= n ; i++)
25     {
26         a[i]=a[i-1]+b[i];
27         cout<<a[i]<<" ";
28     }
29 
30     return 0;

   

 

4.二维差分

    令 a 数组 例如a[2][3] 代表 b[1][1]+b[2][1]+b[1][2]+b[2][2]+b[1][3]+b[2][3];

    

   Q :   如果我们想从a[x1][y1] 到 a[x2][y2] 的值全部加上 c(常数)

   A :   暴力循环是 O(NM),核心步骤是:

     b[x1][y1]+=c

     b[x1][y1+1]-=c

     b[x2][y2+1]-=c

     b[x2+1][y2+1]+=c

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 ll a[1005][1005],b[1005][1005],c,x,y,x2,y2,n,m,q;
 5 
 6 void insert(int x,int y, int x2, int y2,int c)
 7 {
 8     b[x][y] += c;
 9     b[x][y2+1] -= c;
10     b[x2+1][y] -= c;
11     b[x2+1][y2+1] += c;
12 }
13 
14 int main()
15 {
16     cin>>n>>m>>q;
17     for(int i = 1 ; i <= n ; i++)
18         for(int j = 1 ; j <= m ; j++)
19         {
20             cin>>a[i][j];
21             insert(i,j,i,j,a[i][j]);
22         }
23     while(q--)
24     {
25        cin>>x>>y>>x2>>y2>>c;
26         insert(x,y,x2,y2,c);
27     }
28     for(int i = 1 ; i <= n ; i++) {
29         for (int j = 1; j <= m; j++) {
30             a[i][j] = b[i][j] + a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
31             cout << a[i][j] << " ";
32         }
33         cout << "\n";
34     }
35     return 0;
36 }