线段树模板题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,有兴趣的可以登录这俩个平台然后将这几个题做一做

猜你喜欢

转载自blog.csdn.net/LanQiLi/article/details/85322675
今日推荐