前綴與差分

前綴與差分

1. 算法分析

1.1 前綴和

定義
s[n] = \(\sum_{i=1}^na[i]\)c++

遞推關係
s[i] = a[i] + s[i - 1]算法

區間求和
\(\sum_{i=l}^ra[i] = s[r] - s[l - 1]\)數組

1.2 差分

定義
存在兩個數組a(a1, a2, a3,..., an)和b(b1, b2, ... ,bn)
若是ai = b1 + b2 + ... + bi
那麼b稱爲a的差分(好比: b1 = a1, b2 = a2 - a1)spa

做用code

  1. 區間增長->單點修改:當a[l]~a[r]這個區間內元素全加上c時,只須要對b[l] + c, b[r+1] - c便可,由於:b[l] + c:讓a[l]日後的元素所有加上c。b[r+1]-c:防止a[r+1]開始日後的元素加上c
  2. 一開始給ai賦值時,能夠當作是給a[i]~a[i]這段所有加上c

構造
差分的構造能夠選擇: b[i] = a[i] - a[i - 1]ci

變形
差分能夠維護a的變化值,即b[i] = Δai = $ \sum_{i=1}^nb[i] $it

2. 板子

2.1 前綴和

2.1.1 一維前綴和

#include <bits/stdc++.h>

using namespace std;

int const N = 1e5 + 10;
int s[N], a[N];

int main()
{
    int n, k;
    cin >> n >> k;
    // 計算前綴和
    for (int i = 1; i <= n ; ++i)
    {
        scanf("%d", &a[i]);
        s[i] = s[i - 1] + a[i];
    }
    
    // 使用前綴和
    for (int i = 0; i < k ; ++i)
    {
        int l, r;
        scanf("%d %d", &l, &r);
        printf("%d\n", s[r] - s[l -1]);
    }
    return 0;
}

2.1.2 二維前綴和

#include <bits/stdc++.h>

using namespace std;

int const N = 1e3 + 10;
int s[N][N], a[N][N];

int main()
{
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
        {
            scanf("%d", &a[i][j]);
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];  // 前綴和在二維的狀況
        }
    for (int i = 0; i < k; ++i)
    {
        int l1, r1, l2, r2;
        scanf("%d %d %d %d", &l1 ,&r1, &l2, &r2);
        printf("%d\n", s[l2][r2] - s[l2][r1 - 1] - s[l1 - 1][r2] + s[l1 - 1][r1 - 1]);  // 打印結果
    }
    return 0;
}

2.2 差分

2.2.1 一維差分

#include <bits/stdc++.h>

using namespace std;

int const N = 1e5 + 10;
int b[N], n, m, a[N];

// 區間修改
void insert(int l, int r, int c) {
    b[l] += c, b[r + 1] -= c;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);  // 讀入原數組
    for (int i = 1, l, r, c; i <= m; ++i) {  // 不斷讀入區間增長
        scanf("%d %d %d", &l, &r, &c);
        insert(l, r, c);  
    }
    for (int i = 1; i <= n; ++i) {
        b[i] += b[i - 1];  // 計算a[i]的變化值
        printf("%d ", a[i] + b[i]);
    }
    return 0;
}

2.2.1 二維差分

#include <bits/stdc++.h>

using namespace std;

int const N = 1e3 + 10;
int a[N][N], b[N][N];
int n, m, q;

// 區間增長(二維狀況)--和前綴和狀況不同
void insert(int l1, int r1, int l2, int r2, int c)
{
    b[l1][r1] += c;
    b[l2 + 1][r2 + 1] += c;
    b[l2 + 1][r1] -= c;
    b[l1][r2 + 1] -= c;
}

int main() {
    cin >> n >> m >> q;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) scanf("%d", &a[i][j]);  // 讀入原來矩陣
    }
    
    for (int i = 1, l1, r1, l2, r2, c; i <= q; ++i) {
        scanf("%d%d%d%d%d", &l1, &r1, &l2, &r2, &c);  // 區間增長
        insert(l1, r1, l2, r2, c);
    }
    
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];  // 前綴求和,計算變化量
            printf("%d ", a[i][j] + b[i][j]);  // 計算變化後的a[i][j]
        }
        printf("\n");
    }
    return 0;
}
相關文章
相關標籤/搜索