线段树 & 题目

首先说下我写的线段树吧。

我是按照线段树【完全版】那个人的写法来写的,因为网上大多数题解都是按照他的写法来写。

确实比较飘逸,所以就借用了。

节点大小是maxn是4倍,准确来说是大于maxn的2^x次方的最小值的两倍。因为要pushUp的时候会去到一些无用的节点。

就是在叶子节点的时候还pushUp了的话,要去到两个无用的节点,所以是最小值的两倍。

lson 和 rson 用宏定义写了。因为是固定的量。

线段树不必保存自身的区间,因为一边传递过去的时候,在函数里就有区间表示,无谓开多些无用的变量。

pushUp函数,更新当前节点cur的值,其实就是,线段树一般都是处理完左右孩子,然后再递归更新父亲的嘛,这个pushUp函数就是用来更新父亲的。感觉不用这个函数更加清楚明了。

pushDown函数,在lazy--upDate的时候有用,作用是把延迟标记更新到左右节点。

 1 #define lson L, mid, cur << 1
 2 #define rson mid + 1, R, cur << 1 | 1
 3 void pushUp(int cur) {
 4     sum[cur] = sum[cur << 1] + sum[cur << 1 | 1];
 5 }
 6 void pushDown(int cur, int total) {
 7     if (add[cur]) {
 8         add[cur << 1] += add[cur]; //传递去左右孩子
 9         add[cur << 1 | 1] += add[cur]; //  val >> 1 相当于 val / 2
10         sum[cur << 1] += add[cur] * (total - (total >> 1)); //左孩子有多少个节点
11         sum[cur << 1 | 1] += add[cur] * (total >> 1); //一共控制11个,则右孩子有5个
12         add[cur] = 0;
13     }
14 }
15 void build(int L, int R, int cur) {
16     if (L == R) {
17         sum[cur] = a[L];
18         return;
19     }
20     int mid = (L + R) >> 1;
21     build(lson);
22     build(rson);
23     pushUp(cur);
24 }
25 void upDate(int begin, int end, int val, int L, int R, int cur) {
26     if (L >= begin && R <= end) {
27         add[cur] += val;
28         sum[cur] += val * (R - L + 1);     //这里加了一次,后面pushDown就只能用add[cur]的
29         return;
30     }
31 pushDown(cur, R - L + 1);  //这个是必须的,因为下面的pushUp是直接等于的
32 //所以要先把加的,传递去右孩子,然后父亲又调用pushUp,才能保证正确性。
33     int mid = (L + R) >> 1; //一直分解的是大区间,开始时是[1, n]这个区间。
34     if (begin <= mid) upDate(begin, end, val, lson); //只要区间涉及,就必须更新
35     if (end > mid) upDate(begin, end, val, rson);
36     pushUp(cur);
37 }
38 int query(int begin, int end, int L, int R, int cur) {
39     if (L >= begin && R <= end) {
40         return sum[cur];
41     }
42     pushDown(cur, R - L + 1);
43     int ans = 0, mid = (L + R) >> 1;
44     if (begin <= mid) ans += query(begin, end, lson); //只要区间涉及,就必须查询
45     if (end > mid) ans += query(begin, end, rson);
46     return ans;
47 }

成段更新模板

关于成段更新时的upDate函数,中途的pushDown是不能省的,可以看看第三题然后结合我给的数据(数据是poj的大牛发出来的,不是我想的。)关键就在于pushUp函数是直接等于的,你不pushDown,然后pushUp,就会把以前的增加值给抹杀了

HDU 1166    敌兵布阵

单点更新,区间求和

http://acm.hdu.edu.cn/showproblem.php?pid=1166

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#define lson L, mid, cur << 1
#define rson mid + 1, R, cur << 1 | 1
const int maxn = 50000 + 20;
int sum[maxn << 2];
int a[maxn];
void pushUp(int cur) { //更新当前这个节点的信息
    sum[cur] = sum[cur << 1] + sum[cur << 1 | 1];
}
void build(int L, int R, int cur) {
    if (L == R) {
        sum[cur] = a[L];
        return;
    }
    int mid = (L + R) >> 1;
    build(lson);
    build(rson);
    pushUp(cur);
}
void upDate(int pos, int val, int L, int R, int cur) {
    if (L == pos && R == pos) {
        sum[cur] += val;
        return;
    }
    int mid = (L + R) >> 1;
    if (pos <= mid) upDate(pos, val, lson);
    else            upDate(pos, val, rson);
    pushUp(cur);
}
int query(int begin, int end, int L, int R, int cur) { //[L, R]大区间, [begin, end]查询区间
    if (L >= begin && R <= end) { //这个大区间是待查区间的子集
        return sum[cur];
    }
    int mid = (L + R) >> 1;
    int ans = 0;
    if (begin <= mid) ans += query(begin, end, lson);
    if (end > mid)  ans += query(begin, end, rson);
    return ans;
}
int f;
void work() {
    printf("Case %d:\n", ++f);
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }
    build(1, n, 1);
    char cmd[111];
    while (scanf("%s", cmd) != EOF && cmd[0] != ‘E‘) {
        int a, b;
        scanf("%d%d", &a, &b);
        if (cmd[0] == ‘Q‘) {
            printf("%d\n", query(a, b, 1, n, 1));
        } else if (cmd[0] == ‘A‘) {
            upDate(a, b, 1, n, 1);
        } else {
            upDate(a, -b, 1, n, 1);
        }
    }
    return;
}

int main() {
#ifdef local
    freopen("data.txt","r",stdin);
#endif
    int t;
    scanf("%d", &t);
    while (t--) {
        work();
    }
    return 0;
}

HDU 1754  I Hate It

单点更新,区间最值

http://acm.hdu.edu.cn/showproblem.php?pid=1754

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#define lson L, mid, cur << 1
#define rson mid + 1, R, cur << 1 | 1
const int maxn = 200000 + 20;
int mx[maxn << 2];
int a[maxn];
void pushUp(int cur) {
    mx[cur] = max(mx[cur << 1], mx[cur << 1 | 1]);
}
void build(int L, int R, int cur) {
    if (L == R) {
        mx[cur] = a[L]; //就是自己
        return;
    }
    int mid = (L + R) >> 1;
    build(lson);
    build(rson);
    pushUp(cur);
}
void upDate(int pos, int val, int L, int R, int cur) {
    if (L == pos && R == pos) { //精确到这一个点
        mx[cur] = val;
        return;
    }
    int mid = (L + R) >> 1;
    if (pos <= mid) upDate(pos, val, lson);
    else            upDate(pos, val, rson);
    pushUp(cur);
}
int query(int begin, int end, int L, int R, int cur) {
    if (L >= begin && R <= end) {
        return mx[cur];
    }
    int mid = (L + R) >> 1;
    int ans = 0;
    if (begin <= mid) ans = query(begin, end, lson); //区间有涉及,级要查询
    if (end > mid) ans = max(ans, query(begin, end, rson));
    return ans;
}
int n, m;
void work() {
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }
    build(1, n, 1);
    for (int i = 1; i <= m; ++i) {
        char str[11];
        int b, c;
        scanf("%s%d%d", str, &b, &c);
        if (str[0] == ‘Q‘) {
            printf("%d\n", query(b, c, 1, n, 1));
        } else upDate(b, c, 1, n, 1);
    }
}

int main() {
#ifdef local
    freopen("data.txt","r",stdin);
#endif
    while (scanf("%d%d", &n, &m) != EOF) {
        work();
    }
    return 0;
}

POJ 3468 A Simple Problem with Integers

http://poj.org/problem?id=3468

成段更新,区间查询总和,这题记得用LL,

给个数据

10 22
1 2 3 4 5 6 7 8 9 10
Q 4 4
C 1 10 3
C 6 10 3
C 6 9 3
C 8 9 -100
C 7 9 3
C 7 10 3
C 1 10 3
Q 6 10
Q 6 9
Q 8 9
Q 7 9
Q 7 10
Q 1 10
Q 2 4
C 3 6 3
Q 9 9
Q 1 1
Q 5 5
Q 6 6
Q 7 7
Q 6 8

ans

4
-82
-104
-147
-122
-100
-37
27
-73
7
14
21
25
-28

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>

#define lson L, mid, cur << 1
#define rson mid + 1, R, cur << 1 | 1
const int maxn = 100000 + 20;
LL sum[maxn << 2];
LL add[maxn << 2];
int a[maxn];
void pushUp(int cur) {
    sum[cur] = sum[cur << 1] + sum[cur << 1 | 1];
}
void pushDown(int cur, int total) {
    if (add[cur]) {
        add[cur << 1] += add[cur];
        add[cur << 1 | 1] += add[cur]; //  val >> 1 相当于 val / 2
        sum[cur << 1] += add[cur] * (total - (total >> 1)); //左孩子有多少个节点
        sum[cur << 1 | 1] += add[cur] * (total >> 1); //一共控制11个,则右孩子有5个
        add[cur] = 0;
    }
}
void build(int L, int R, int cur) {
    if (L == R) {
        sum[cur] = a[L];
        return;
    }
    int mid = (L + R) >> 1;
    build(lson);
    build(rson);
    pushUp(cur);
}
void upDate(int begin, int end, LL val, int L, int R, int cur) {
    if (L >= begin && R <= end) {
        add[cur] += val;//这里加了一次,后面pushDown就只能用add[cur]的
        sum[cur] += val * (R - L + 1); //控制的节点数目
        return;
    }
    pushDown(cur, R - L + 1);
    int mid = (L + R) >> 1;
    if (begin <= mid) upDate(begin, end, val, lson);
    if (end > mid) upDate(begin, end, val, rson);
    pushUp(cur);
}
LL query(int begin, int end, int L, int R, int cur) {
    if (L >= begin && R <= end) {
        return sum[cur];
    }
    pushDown(cur, R - L + 1);
    LL ans = 0, mid = (L + R) >> 1;
    if (begin <= mid) ans += query(begin, end, lson);
    if (end > mid) ans += query(begin, end, rson);
    return ans;
}
void work() {
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }
    build(1, n, 1);
    char str[11];
    for (int i = 1; i <= q; ++i) {
        scanf("%s", str);
        int L, R, val;
        if (str[0] == ‘Q‘) {
            scanf("%d%d", &L, &R);
            printf("%I64d\n", query(L, R, 1, n, 1));
        } else {
            scanf("%d%d%d", &L, &R, &val);
            upDate(L, R, val, 1, n, 1);
        }
    }
    return;
}
int main() {
#ifdef local
    freopen("data.txt","r",stdin);
#endif
    work();
    return 0;
}

时间: 2024-07-31 14:22:45

线段树 & 题目的相关文章

线段树题目汇总

区间合并部分: POJ 3667 Hotel 求某大于等于a的最长区间 #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #define LEN 50001<<2 using namespace std; struct Line_tree { //分别表示以当前区间为左端点的最长连续空白区间,为右端点的最长连续空白区间,该区间的最长连续空白区

线段树题目总结

一.单点更新 1.hdu1166 敌兵布阵:有N个兵营,每个兵营都给出了人数ai(下标从1开始),有四种命令,(1)"Addij",表示第i个营地增加j人.(2)"Sub i j",表示第i个营地减少j人.(3)"Query ij",查询第i个营地到第j个营地的总人数.(4)"End",表示命令结束.解题报告Here. 2.hdu1754 I Hate It:给你N个数,M个操作,操作分两类.(1)"QAB"

线段树基础

关于线段树的原理学习,可以参看杨弋大牛的论文<线段树>以及刘汝佳老师的<算法竞赛入门经典(训练指南)>,代码风格学习hzwer或者notonlysuccess均可. 一.单点更新 最基础的线段树 题目:codevs1080 链接:http://codevs.cn/problem/1080/ 分析:最简单的线段树,单点更新,区间求和 1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4

PKU-3468 simple problem with integer 线段树 LL坑点(欢迎讨论)

You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.InputThe first

HDU 4893 线段树裸题

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2512    Accepted Submission(s): 751 Problem Description Recently, Doge got a funny birthday present from his new friend, Pro

洛谷 P2253 好一个一中腰鼓 --线段树

P2253 好一个一中腰鼓 --线段树 题目背景 话说我大一中的运动会就要来了,据本班同学剧透(其实早就知道了),我萌萌的初二年将要表演腰鼓[喷],这个无厘头的题目便由此而来. Ivan乱入:“忽一人大呼:‘好一个安塞腰鼓!’满座寂然,无敢哗者,遂与外人间隔.” 题目描述 设想一下,腰鼓有两面,一面是红色的,一面是白色的.初二的苏大学神想给你这个oier出一道题.假设一共有N(1<=N<=20,000)个同学表演,表演刚开始每一个鼓都是红色面朝向观众,舞蹈老师会发出M(1<=M<=

杭电 HDU ACM 2795 Billboard(线段树伪装版)

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 14144    Accepted Submission(s): 6058 Problem Description At the entrance to the university, there is a huge rectangular billboard of

HDU - 3308 - LCIS (线段树 - 区间合并)

题目传送:LCIS 线段树,区间合并,一次过啦,没有纠结,这几天过的最愉快的一个题 思路:求最长连续上升子序列,外带单点更新,经典的线段树题目.具体看代码注释 AC代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cmath> #include <queue> #include <stack>

hdu4288 离线处理线段树

http://acm.hdu.edu.cn/showproblem.php?pid=4288 Problem Description In mathematics and computer science, an algorithm describes a set of procedures or instructions that define a procedure. The term has become increasing popular since the advent of che