1. 程式人生 > >線段樹模板題5題

線段樹模板題5題

題目描述

如題,已知一個數列,你需要進行下面兩種操作:

1.將某區間每一個數加上x

2.求出某區間每一個數的和

輸入輸出格式

輸入格式:

 

第一行包含兩個整數N、M,分別表示該數列數字的個數和操作的總個數。

第二行包含N個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。

接下來M行每行包含3或4個整數,表示一個操作,具體如下:

操作1: 格式:1 x y k 含義:將區間[x,y]內每個數加上k

操作2: 格式:2 x y 含義:輸出區間[x,y]內每個數的和

 

輸出格式:

 

輸出包含若干行整數,即為所有操作2的結果。

 

輸入輸出樣例

輸入樣例#1: 複製

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

輸出樣例#1: 複製

11
8
20

說明

時空限制:1000ms,128M

資料規模:

對於30%的資料:N<=8,M<=10

對於70%的資料:N<=1000,M<=10000

對於100%的資料:N<=100000,M<=100000

(資料已經過加強^_^,保證在int64/long long資料範圍內)

樣例說明:

 

#include<iostream>
using namespace std;
const int maxn = 100010;
int n, m, p, x, a, b;
long long ans;
struct Node {
    long long l, r, w, f;
}node[4 * maxn + 10];

void build(int l, int r, int k) {
    node[k].r = r;
    node[k].l = l;
    if (l == r) {
        cin >> node[k].w;
        return;
    }
    int mid = (r + l) / 2;
    build(l, mid, k * 2);
    build(mid + 1, r, k * 2 + 1);
    node[k].w = node[k * 2].w + node[k * 2 + 1].w;
}

void down(int k) {
    node[k * 2].f += node[k].f;
    node[k * 2 + 1].f += node[k].f;
    node[k * 2].w += (node[k * 2].r - node[k * 2].l + 1) * node[k].f;
    node[k * 2 + 1].w += (node[k * 2 + 1].r - node[k * 2 + 1].l + 1) * node[k].f;
    node[k].f = 0;
}

void add(int k) {
    if (node[k].l >= a && node[k].r <= b) {
        node[k].w += (node[k].r -node[k].l + 1) * x;
        node[k].f += x;
        return;
    }
    if (node[k].f) down(k);
    int mid = (node[k].l + node[k].r) / 2;
    if (a <= mid) {
        add(k * 2);
    }
    if (b > mid) {
        add(k * 2 + 1);
    }
    node[k].w = node[k * 2].w + node[k * 2 + 1].w;
}

void sum(int k) {
    if (node[k].l >= a && node[k].r <= b) {
        ans += node[k].w;
        return;
    }
    if (node[k].f) down(k);
    int mid = (node[k].l + node[k].r) / 2;
    if (a <= mid) {
        sum(k * 2);
    }
    if (b > mid) {
        sum(k * 2 + 1);
    }
}

int main () {
    cin >> n >> m;
    build(1, n, 1);
    for (int i = 1; i <= m; i++) {
        cin >> p;
        ans = 0;
        if (p == 1) {
            cin >> a >> b >> x;
            add(1);
        } else {
            cin >> a >> b;
            sum(1);
            cout << ans << endl;
        }
    }
    return 0;
}

 

 

題目描述

如題,已知一個數列,你需要進行下面三種操作:

1.將某區間每一個數乘上x

2.將某區間每一個數加上x

3.求出某區間每一個數的和

輸入輸出格式

輸入格式:

 

第一行包含三個整數N、M、P,分別表示該數列數字的個數、操作的總個數和模數。

第二行包含N個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。

接下來M行每行包含3或4個整數,表示一個操作,具體如下:

操作1: 格式:1 x y k 含義:將區間[x,y]內每個數乘上k

操作2: 格式:2 x y k 含義:將區間[x,y]內每個數加上k

操作3: 格式:3 x y 含義:輸出區間[x,y]內每個數的和對P取模所得的結果

 

輸出格式:

 

輸出包含若干行整數,即為所有操作3的結果。

 

輸入輸出樣例

輸入樣例#1: 複製

5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4

輸出樣例#1: 複製

17
2

說明

時空限制:1000ms,128M

資料規模:

對於30%的資料:N<=8,M<=10

對於70%的資料:N<=1000,M<=10000

對於100%的資料:N<=100000,M<=100000

(資料已經過加強^_^)

樣例說明:

故輸出應為17、2(40 mod 38=2)

 

思路:該題比較複雜,因為存在倆個不同的改變區域的方案,因此我設計了倆個懶標籤,同時因為要取模,所以有倆種方案的優先順序可以選擇,一種是先加後乘,另外一種是先乘後加,都是用在更新結點上,如果我們採取前者的方案可能存在精度損失,所以我們選擇後者,其他的具體看程式碼,注意down裡面的程式碼和一個懶標記很不一樣,其他也有一些部分需要稍微改改,總的來說這個題目真的是一個很不錯的模板題

#include<iostream>
using namespace std;
const int maxn = 100010;
struct Node{
    long long l, r, w, add, mul;
}node[4 * maxn + 10];

long long a, b, x, n, m, z, p, ans;
void build(int l, int r, int k) {
    node[k].r = r;
    node[k].l = l;
    node[k].mul = 1;
    if (l == r) {
        cin >> node[k].w;
        return;
    }
    int mid = (l + r) / 2;
    build(l, mid, k * 2);
    build(mid + 1, r, k * 2 + 1);
    node[k].w = (node[k * 2].w + node[k * 2 + 1].w) % p;
}

void down(int k) {
    node[k * 2].w = (node[k].mul * node[k * 2].w + node[k].add * (node[k * 2].r - node[k * 2].l + 1)) % p;
    node[k * 2 + 1].w = (node[k].mul * node[k * 2 + 1].w + node[k].add * (node[k * 2 + 1].r - node[k * 2 + 1].l + 1)) % p;
    node[k * 2].mul = (node[k].mul * node[k * 2].mul) % p;
    node[k * 2 + 1].mul = (node[k].mul * node[k * 2 + 1].mul) % p;
    node[k * 2].add = (node[k].mul * node[k * 2].add + node[k].add) % p;
    node[k * 2 + 1].add = (node[k].mul * node[k * 2 + 1].add + node[k].add) % p;
    node[k].mul = 1;
    node[k].add = 0;
}

void add(int k) {
    if (a <= node[k].l && b >= node[k].r) {
        node[k].w = (node[k].w + (node[k].r - node[k].l + 1) * x) % p;
        node[k].add = (node[k].add + x) % p;
        return;
    }
    if(node[k].add || node[k].mul != 1) down(k);
    int mid = (node[k].l + node[k].r) / 2;
    if (a <= mid) {
        add(k * 2);
    }
    if (b > mid) {
        add(k * 2 + 1);
    }
    node[k].w = (node[k * 2].w + node[k * 2 + 1].w) % p;
}

void mul(int k) {
    if (a <= node[k].l && b >= node[k].r) {
        node[k].w = (node[k].w * x) % p;
        node[k].mul = (node[k].mul * x) % p;
        node[k].add = (x * node[k].add) % p;
        return;
    }
    if(node[k].add || node[k].mul != 1) down(k);
    int mid = (node[k].l + node[k].r) / 2;
    if (a <= mid) {
        mul(k * 2);
    }
    if (b > mid) {
        mul(k * 2 + 1);
    }
    node[k].w = (node[k * 2].w + node[k * 2 + 1].w) % p;
}

void sum(int k) {
    if (a <= node[k].l && b >= node[k].r) {
        ans = (node[k].w + ans) % p;
        return;
    }
    if(node[k].add || node[k].mul != 1) down(k);
    int mid = (node[k].l + node[k].r) / 2;
    if (a <= mid) {
        sum(k * 2);
    }
    if (b > mid) {
        sum(k * 2 + 1);
    }
}


int main() {
    cin >> n >> m >> p;
    build(1, n, 1);
    for (int i = 1; i <= m; i++) {
        cin >> z;
        if (z == 1) {
            cin >> a >> b >> x;
            mul(1);
        } else if (z == 2) {
            cin >> a >> b >> x;
            add(1);
        } else {
            cin >> a >> b;
            ans = 0;
            sum(1);
            cout << ans << endl;
        }
    }
    return 0;
}

 

 

一行N個方格,開始每個格子裡都有一個整數。現在動態地提出一些問題和修改:提問的形式是求某一個特定的子區間[a,b]中所有元素的和;修改的規則是指定某一個格子x,加上或者減去一個特定的值A。現在要求你能對每個提問作出正確的回答。1≤N<100000,,提問和修改的總數m<10000條。

輸入描述 Input Description

輸入檔案第一行為一個整數N,接下來是n行n個整數,表示格子中原來的整數。接下一個正整數m,再接下來有m行,表示m個詢問,第一個整數表示詢問代號,詢問代號1表示增加,後面的兩個數x和A表示給位置X上的數值增加A,詢問代號2表示區間求和,後面兩個整數表示a和b,表示要求[a,b]之間的區間和。

輸出描述 Output Description

共m行,每個整數

樣例輸入 Sample Input

6

3

4

1 3 5

2 1 4

1 1 9

2 2 6

樣例輸出 Sample Output

22

22

資料範圍及提示 Data Size & Hint

1≤N≤100000, m≤10000 。

#include<iostream>
using namespace std;
const int maxn = 100000;
int n, x, y, p, ans, m;
struct node {
    int l, r, w;
}tree[4 * maxn + 10];

void build(int l, int r, int k) {
    tree[k].l = l;
    tree[k].r = r;
    if (l == r) {
        cin >> tree[k].w;
        return;
    }
    int mid = (l + r) / 2;
    build(l, mid, k * 2);
    build(mid + 1, r, k * 2 + 1);
    tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
}

void add(int k) {
    if (tree[k].l == tree[k].r) {
        tree[k].w += y;
        return;
    }
    int mid = (tree[k].l + tree[k].r) / 2;
    if (x <= mid) add(k * 2);
    else add(k * 2 + 1);
    tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
}

void sum(int k) {
    if (tree[k].l >= x && tree[k].r <= y) {
        ans += tree[k].w;
        return;
    }
    int mid = (tree[k].l + tree[k].r) / 2;
    if (x <= mid) sum(k * 2);
    if (y > mid) sum(k * 2 + 1);
}

int main() {
    cin >> n;
    build(1, n, 1);
    cin >> m;
    for (int i = 1; i <= m; i++) {
        cin >> p >> x >> y;
        ans = 0;
        if (p == 1) {
            add(1);
        } else {
            sum(1);
            cout << ans << endl;
        }
    }
    return 0;
}

 

 

題目描述 Description

給你N個數,有兩種操作


1:給區間[a,b]的所有數都增加X


2:詢問第i個數是什麼?

輸入描述 Input Description

第一行一個正整數n,接下來n行n個整數,再接下來一個正整數Q,表示操作的個數. 接下來Q行每行若干個整數。如果第一個數是1,後接3個正整數a,b,X,表示在區間[a,b]內每個數增加X,如果是2,後面跟1個整數i, 表示詢問第i個位置的數是多少。

輸出描述 Output Description

對於每個詢問輸出一行一個答案

樣例輸入 Sample Input

3

1

2

3

2

1 2 3 2

2 3

樣例輸出 Sample Output

5

資料範圍及提示 Data Size & Hint

資料範圍

1<=n<=100000

1<=q<=100000

 

#include<iostream>
using namespace std;
const int maxn = 100010;
int n, m, p, x, a, b, ans;
struct node {
    int l, r, w, f;
}tree[4 * maxn + 10];

void build(int l, int r, int k) {
    tree[k].l = l;
    tree[k].r = r;
    if (l == r) {
        cin >> tree[k].w;
        return;
    }
    int mid = (l + r) / 2;
    build(l, mid, k * 2);
    build(mid + 1, r, k * 2 + 1);
    tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
}

void down(int k) {
    tree[k * 2].f += tree[k].f;
    tree[k * 2 + 1].f += tree[k].f;
    tree[2 * k].w += tree[k].f * (tree[k * 2].r - tree[k * 2].l + 1);
    tree[2 * k + 1].w += tree[k].f * (tree[k * 2 + 1].r - tree[k * 2 + 1].l + 1);
    tree[k].f = 0;
}

void add(int k) {
    if (tree[k].l >= a && tree[k].r <= b) {
        tree[k].w += (tree[k].r - tree[k].l + 1) * x;
        tree[k].f += x;
        return;
    }
    if (tree[k].f) down(k);
    int mid = (tree[k].l + tree[k].r) / 2;
    if (a <= mid) add(k * 2);
    if (b > mid) add(k * 2 + 1);
    tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
}



void ask(int k) {
    if (tree[k].l == tree[k].r) {
        ans = tree[k].w;
        return;
    }
    if (tree[k].f) down(k);
    int mid = (tree[k].l + tree[k].r) / 2;
    if (x <= mid) ask(k * 2);
    else ask(k * 2 + 1);
}


int main () {
    cin >> n;
    build(1, n, 1);
    cin >> m;
    for (int i = 0; i < m; i++) {
        cin >> p;
        if (p == 1) {
            cin >> a >> b >> x;
            add(1);
        } else {
            cin >> x;
            ask(1);
            cout << ans << endl;
        }
    }
    return 0;
}

 

題目描述 Description

給你N個數,有兩種操作:


1:給區間[a,b]的所有數增加X


2:詢問區間[a,b]的數的和。

輸入描述 Input Description

第一行一個正整數n,接下來n行n個整數,

 

再接下來一個正整數Q,每行表示操作的個數,

 

如果第一個數是1,後接3個正整數,

 

表示在區間[a,b]內每個數增加X,如果是2,

 

表示操作2詢問區間[a,b]的和是多少。

 

pascal選手請不要使用readln讀入

輸出描述 Output Description

對於每個詢問輸出一行一個答案

樣例輸入 Sample Input

3

1

2

3

2

1 2 3 2

2 2 3

樣例輸出 Sample Output

9

資料範圍及提示 Data Size & Hint

資料範圍

1<=n<=200000

1<=q<=200000

 

#include<iostream>
using namespace std;
const int maxn = 200010;
int n, m, p, x, a, b;
long long ans;
struct Node {
    long long l, r, w, f;
}node[4 * maxn + 10];

void build (int l, int r, int k) {
    node[k].l = l;
    node[k].r = r;
    if (l == r) {
        cin >> node[k].w;
        return;
    }
    int mid = (l + r) / 2;
    build(l, mid, k * 2);
    build(mid + 1, r, k * 2 + 1);
    node[k].w = node[k * 2].w + node[k * 2 + 1].w;
}

void down(int k) {
    node[2 * k].f += node[k].f;
    node[2 * k + 1].f += node[k].f;
    node[2 * k].w += (node[2 * k].r - node[2 * k].l + 1) * node[k].f;
    node[2 * k + 1].w += (node[2 * k + 1].r - node[2 * k + 1].l + 1) * node[k].f;
    node[k].f = 0;
}

void add(int k) {
    if (node[k].l >= a && b >= node[k].r) {
        node[k].w += (node[k].r - node[k].l + 1) * x;
        node[k].f += x;
        return;
    }
    if (node[k].f) down(k);
    int mid = (node[k].l + node[k].r) / 2;
    if (a <= mid) {
        add(k * 2);
    }
    if (b > mid) {
        add(k * 2 + 1);
    }
    node[k].w = node[k * 2].w + node[k * 2 + 1].w;
}

void sum(int k) {
    if (node[k].l >= a && node[k].r <= b) {
        ans += node[k].w;
        return;
    }
    if (node[k].f) down(k);
    int mid = (node[k].l + node[k].r) / 2;
    if (a <= mid) {
        sum(k * 2);
    }
    if (b > mid) {
        sum(k * 2 + 1);
    }
}

int main() {
    cin >> n;
    build(1, n, 1);
    cin >> m;
    for (int i = 0; i < m; i++) {
        cin >> p;
        ans = 0;
        if (p == 1) {
            cin >> a >> b >> x;
            add(1);
        } else {
            cin >> a >> b;
            sum(1);
            cout << ans << endl;
        }
    }
    return 0;
}

總結:其實這類的模板題寫多了就熟練了,可以進行更近一步的題目的針對訓練了,題目來源自洛古語codevs,有興趣的可以登入這倆個平臺然後將這幾個題做一做