kuangbin 线段树 - POJ - 3264 Balanced Lineup (线段树单点修改模板题)

kuangbin 线段树 - POJ - 3264 Balanced Lineup (线段树单点修改模板题)

总题单 week 3 [kuangbin带你飞] 题单 最小生成树 + 线段树 Click here ~~
https://blog.csdn.net/m0_46272108/article/details/108980362

Descriptions:

For the daily milking, Farmer John’s N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple,
he will take a contiguous range of cows from the milking lineup to
play the game. However, for all the cows to have fun they should not differ too much in height.
Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.

题目描述

一片绿地的N(1≤N≤50000)颗树排成一排,在一个美好的下午,园艺师傅接到任务对这N棵树修剪,园艺师傅已经知道这N颗树的高度H ( 1 ≤ h e i g h t ≤ 1000000 ) (1≤height≤1000000) (1height1000000) 。园艺师傅街道的任务有 Q ( 1 ≤ Q ≤ 200000 ) Q(1≤Q≤200000) Q(1Q200000) 个小任务,对于每一个小任务,园艺师傅都要知道特定区间最高的树和最矮的树的高度差。园艺师傅不会算法,这个任务对他有些难,你能帮助他吗?

Input

Line 1: Two space-separated integers, N and Q.
Lines 2:N+1: Line i+1 contains a single integer that is the height of cow i
Lines N+2:N+Q+1: Two integers A and B (1 ≤ A ≤ B ≤ N),representing the range of cows
from A to B inclusive.
第一行为 N ( 1 ≤ N ≤ 50000 ) N(1≤N≤50000) N(1N50000) Q ( 1 ≤ Q ≤ 200000 ) Q(1≤Q≤200000) Q(1Q200000);. 从第2行到第N+1行,每行一个数字,
表示第i棵树的高度 ( 1 ≤ h e i g h t ≤ 1000000 ) (1≤height≤1000000) (1height1000000); 从第N+2行到第N+Q+1行,每行两个整数A和B ( 1 ≤ A ≤ B ≤ N ) (1≤A≤B≤N) (1ABN)
表示从第A颗树到第B颗树这个区间所有的树。

Output

Lines 1 Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.

从第一行到第Q行,每行一个整数,表示从第A棵树到第B棵树之间,最高树与最矮树的高度差。

Sample Input

6 3
1
7
3
4
2
5
1 5
4 6
2 2

Sample Output

6
3
0

本题是查询区间内最大值和最小值的差(开两个变量即可)
理解 线段树的单点修改 即可 (并且代码有详细解释)
关于 线段树的单点修改 ,可以查看这篇文章(内有模板题 HDU - 1166 敌兵布阵 及图文详解):
线段树相关知识点及模板题 HDU - 1166 敌兵布阵:https://blog.csdn.net/m0_46272108/article/details/108955623

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<algorithm>

#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define ll long long
//#define int ll
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
    
    
	int w = 1, s = 0;
	char ch = getchar();
	while (ch < '0' || ch>'9') {
    
     if (ch == '-') w = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') {
    
     s = s * 10 + ch - '0';    ch = getchar(); }
	return s * w;
//最大公约数
}int gcd(int x,int y) {
    
    
    if(x<y) swap(x,y);//很多人会遗忘,大数在前小数在后
    //递归终止条件千万不要漏了,辗转相除法
    return x % y ? gcd(y, x % y) : y;
}
int lcm(int x,int y)//计算x和y的最小公倍数
{
    
    
    return x * y / gcd(x, y);//使用公式
}
//------------------------ 以上是我常用模板与刷题几乎无关 ------------------------//

const int N = 50010;
int Maxval, Minval;
struct Node{
    
    
    int l, r;//左端点,右端点
    int Maxval;//区间[l, r]的最大值
    int Minval;//区间[l, r]的最小值
}tr[N << 2];

//由子节点的信息来计算父节点的信息
void pushup(int cur, int val){
    
    
    tr[cur].Maxval = max(tr[cur].Maxval, val);
    tr[cur].Minval = min(tr[cur].Minval, val);
}

//cur代表当前节点,
void build(int cur, int l, int r){
    
    
    //当前结点的左右儿子分别是tr[cur].l   tr[cur].r
    tr[cur] = {
    
    l, r};
    //如果已经是叶结点return
    
    tr[cur].Minval = inf;
    tr[cur].Maxval = -inf;
    //求一下当前区间的中点
    int mid = l + r >> 1;
    
    if(l == r) {
    
    
        return;
    } else {
    
    
        //递归建立左边区间
    build(cur << 1, l, mid);
    //递归建立右边区间
    build(cur << 1 | 1, mid + 1, r);
    // pushup(cur);
    }
}

//[l, r]查询区间   cur代表当前线段树里面的端点。
void query(int cur, int l, int r) {
    
    
    
    if(tr[cur].Maxval <= Maxval && tr[cur].Minval >= Minval) 
        return;
    //①情况[TL,TR] ⊂ [L,R]
    //树中节点,已经被完全包含在[l, r]中了。
    if (tr[cur].l >= l && tr[cur].r <= r) {
    
    
        Minval = min(tr[cur].Minval, Minval);
        Maxval = max(tr[cur].Maxval, Maxval);
        return;
    }
    
    int mid = tr[cur].l + tr[cur].r >> 1;
    
    //判断与左边有没有交集
    if (r <= mid) {
    
    
        query(cur << 1, l, r);
    } else if (l > mid) {
    
    
    //这里为什么是 r > mid,因为划分的区间是[l, mid][mid + 1, r],所以要用>而不能=
    //判断与右边有没有交集
        //为什么要取max?
        query(cur << 1 | 1, l, r);
    } else {
    
    
        query(cur << 1, l, r);
        query(cur << 1 | 1, l, r);
    }
}

//cur代表当前线段树里面的端点。tar代表要修改的位置
void modify(int cur, int tar, int val) {
    
    
    int l = tr[cur].l, r = tr[cur].r;
    //如果当前节点就是叶节点,那么直接修改就可以了
    if (tr[cur].l == tar && tr[cur].r == tar) {
    
    
        tr[cur].Maxval = val;
        tr[cur].Minval = val;
        return;
    }
    int mid = l + r >> 1;
    if (tar <= mid) {
    
    
        modify (cur << 1, tar, val);
    } else {
    
    
        modify (cur << 1 | 1, tar, val);
    }
    //递归完之后,要更新到父节点。
    //pushup就是更新父节点的信息
    pushup(cur, val);
}

int main()
{
    
    
    int n, q;
    int a, b;
    while (~scanf("%d%d", &n, &q)) {
    
    
        build(1, 1, n);
        for (int i = 1; i <= n; ++i) {
    
    
            a = read();
            modify(1, i, a);
        }
        for (int i = 1; i <= q; ++i) {
    
    
            a = read(), b = read();
            Minval = inf, Maxval = -inf;
            query(1, a, b);
            printf("%d\n", Maxval - Minval);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_46272108/article/details/108962485