线段树

2 篇文章 0 订阅
1 篇文章 0 订阅

线段树

线段树是一棵二叉树,每个节点记录相应区间的信息
在这里插入图片描述

  • 维护最大值
    在这里插入图片描述
    解决的问题
  1. 单点更新,求区间最值,区间和
  2. 成段更新,区间求和
  3. 二维空间覆盖问题(求覆盖面积,周长)

实现参考NOTONLYSUCCESS

  • 单点更新,区间求和

hdu1166 敌兵布阵

#include <stdio.h>

//maxn是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn的最小2x的两倍
const int maxn = 56000 << 2;
int sum[maxn];

//PushUP(int rt)是把当前结点的信息更新到父结点
//rt表示当前子树的根(root),也就是当前所在的结点
void PushUP(int rt) {
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(int l, int r, int rt) {
    if (l == r) {
        scanf("%d", sum+rt);
        return ;
    }
    int m = (l + r) >> 1;
    build(l, m, rt<<1);
    build(m+1, r, rt<<1|1);
    PushUP(rt);
}
//add i,j
//sub i,j
void update(int p, int add, int l, int r, int rt) {
    if (l == r) {
        sum[rt] += add;
        return;
    }
    int m = (l + r) >> 1;
    if (p <= m) update(p, add, l, m, rt<<1);
    else update(p, add, m+1, r, rt<<1|1);
    PushUP(rt);
}
int Query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) {
        return sum[rt];
    }
    int m = (l + r) >> 1;
    int ret = 0;
    if (L <= m) ret += Query(L, R, l, m , rt<<1);
    if (R > m) ret += Query(L, R, m+1, r, rt<<1|1);
    return ret;
}

void PushDown(int rt) {

}

int main() {
    int T, n;
    scanf("%d", &T);
    for (int cas = 1; cas <= T; cas++) {
        printf ("Case %d:\n", cas);
        scanf("%d", &n);
        build(1, n, 1);
        char op[10];
        while(scanf("%s", op)) {
            if (op[0] == 'E') break;
            int a, b;
            scanf("%d%d", &a, &b);
            if (op[0] == 'Q') printf("%d\n", Query(a, b,1,n,1));
            else if (op[0] == 'S') update(a, -b, 1, n, 1);
            else update(a, b, 1, n, 1);
        }
    }
    return 0;
}

hdu 1394 Minimum Inversion Number
这个用来求逆序对了

#include <stdio.h>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

/*
author: Steven
date: 2018.10.18

Because the n integers is from 0 - n-1;
In the order of input, the input number a[i], if there is k numbers before a[i], the inverse number about a[i] is k in the before i+1 numbers;
So we can sum the inverse number as the input.
The segment tree maintenance the k, the numbers more than a[i] in the intervals from a[i] to n-1 in the before i+1 numbers.
If the first number is a[i], the inverse number about a[i] is a[i] since there are a[i] numbers less than a[i].
Move a[i] to the end of sequence, the inverse number about a[i] is n-a[i]-1, so the increase inverse number is (n - a[i] -1 -a[i]);
Then we can find the min inverse number.
*/
const int maxn = 5500;

class SegTree{
private:
    int sum[maxn<<2], n, a[maxn], Min;
public:
    void PushUp (int rt) {
        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    }
    void build(int l, int r, int rt) {
        if (l == r) {
            sum[rt] = 0;
            return ;
        }
        int mid = (l+r) >> 1;
        build(l, mid, rt<<1);
        build(mid+1, r, rt<<1|1);
    }
    int Query(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) return sum[rt];
        int mid = (l + r) >> 1, ret = 0;
        if (mid >= L) ret += Query(L, R, l, mid, rt<<1);
        if (mid <= R) ret += Query(L, R, mid+1, r, rt<<1|1);
        return ret;
    }
    void Update(int p, int l, int r, int rt) {
        if (l == r ) {
            sum[rt] ++;
            return ;
        }
        int mid = (l + r) >> 1;
        if (p <= mid) Update(p, l, mid, rt<<1);
        else Update(p, mid+1, r, rt<<1|1);
        PushUp(rt);
    }
    void input() {
        while (cin >> n) {
        //build(0, n-1, 1);
        memset(sum, 0, sizeof(sum));
        int tmp_min = 0;
        for (int i = 0; i<n; i++) {
            cin >> a[i];
            tmp_min += Query(a[i], n-1, 0, n-1, 1);
            Update(a[i], 0, n-1, 1);
        }
        Min = tmp_min;
        for (int i = 0; i<n ;i ++) {
            tmp_min += n - 1 - a[i] - a[i];
            Min = min(tmp_min, Min);
        }
        cout << Min << endl;
        }
    }
};
int main() {
    SegTree S;
    S.input();
    return 0;
}


  • 单点更新,区间最值

hdu1754 I Hate It

#include <stdio.h>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

//maxn是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn的最小2x的两倍
const int maxn = 222222 << 2;

class SegTree{
private:
    int sum[maxn];
    int n, m;

public:
    //PushUP(int rt)是把当前结点的信息更新到父结点
    //rt表示当前子树的根(root),也就是当前所在的结点
    void input() {
        while (scanf("%d%d", &n, &m)!=EOF){
            //memset(sum, 0 , sizeof(sum));
            int i;
            build(1, n, 1);
            char op[10];
            while(m--) {
                scanf("%s", op);
                int a, b;
                scanf("%d%d", &a, &b);
                if (op[0] == 'Q') printf("%d\n", Query(a, b,1,n,1));
                else update(a, b, 1, n, 1);
            }
        }
    }
    int Max(const int a, const int b) {
        if (a > b) return a;
        return b;
    }
    void PushUP(int rt) {
        sum[rt] = max(sum[rt<<1], sum[rt<<1|1]);
    }
    void build(int l, int r, int rt) {
        if (l == r) {
            scanf("%d", sum+rt);
            return ;
        }
        int m = (l + r) >> 1;
        build(l, m, rt<<1);
        build(m+1, r, rt<<1|1);
        PushUP(rt);
    }
    //add i,j
    //sub i,j
    void update(int p, int add, int l, int r, int rt) {
        if (l == r) {
            sum[rt] = add;
            return;
        }
        int m = (l + r) >> 1;
        if (p <= m) update(p, add, l, m, rt<<1);
        else update(p, add, m+1, r, rt<<1|1);
        PushUP(rt);
    }
    int Query(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) {
            return sum[rt];
        }
        int m = (l + r) >> 1;
        int ret = 0;
        if (L <= m) ret = max(ret, Query(L, R, l, m , rt<<1));
        if (R > m) ret = max(ret, Query(L, R, m+1, r, rt<<1|1));
        return ret;
    }

    void PushDown(int rt) {

    }
};
int main() {
    SegTree S;
    S.input();
    return 0;
}

hdu2795 Billboard

#include <stdio.h>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

/*
author: Steven
date: 2018.10.29

Every post, if the maxW >= wi,we can cost O(1) to find the maxW, then find the top row wk which >= w[i],
better time complexity is O(lgn), segment tree is one of them. w[k]-w[i] and go on.
segment tree maintenance the maxW corresponding intervals.
Attention, the node of segment tree > n is waste.
*/
const int maxn = 20200;

class SegTree{
private:
    int sum[maxn<<2], n, h,w;
public:
    void PushUp (int rt) {
        sum[rt] = max(sum[rt<<1], sum[rt<<1|1]);
    }
    void build(int l, int r, int rt) {
        sum[rt] = w;
        if (l == r) {
            return ;
        }
        int mid = (l+r) >> 1;
        build(l, mid, rt<<1);
        build(mid+1, r, rt<<1|1);
    }
    int Query(int wi, int l, int r, int rt) {
        if (l == r) {
            sum[rt] -= wi;
            return l;
        }
        int mid = (l + r) >> 1, ret = -1;
        if (sum[rt<<1] >= wi) ret = Query(wi, l, mid, rt<<1);
        else ret = Query(wi, mid+1, r, rt<<1|1);
        PushUp(rt);
        return ret;
    }
    void Update(int p, int l, int r, int rt) {
        if (l == r ) {
            sum[rt] ++;
            return ;
        }
        int mid = (l + r) >> 1;
        if (p <= mid) Update(p, l, mid, rt<<1);
        else Update(p, mid+1, r, rt<<1|1);
        PushUp(rt);
    }
    void input() {
        while (cin >> h >> w >>n) {
            h = min(h, n);
            build(1, h, 1);
            int ai;
            for (int i = 0; i<n; i++) {
                scanf("%d", &ai);
                if (ai > sum[1]) puts("-1");
                else printf("%d\n", Query(ai, 1, h, 1));
            }
        }
    }
};
int main() {
    SegTree S;
    S.input();
    return 0;
}

  • 单点更新, 区间k值
    区间k值指求区间第k个值,这里例子是第k个空闲的值
    poj2828 Buy Tickets

倒插法(逆向思维)+第k空闲

#include <stdio.h>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

/*
author: Steven
date: 2018.11.05

the priority of the later is higher than the one go first, there needs reverse thinking.
just handle the queue from end to begin.
next, for the pair (Posi and Vali), we should find the Posi_th empty location to locate Vali.
the segment tree maintain the number of empty location, the number of interval (l,r) is (r - l + 1),
when handle pair (Posi, Vali), the node of the path the pair cross,just subtract 1.
*/
const int maxn = 202000;

class SegTree{
private:
    int sum[maxn<<2], n, a[maxn][2], b[maxn];
public:
    void PushUp (int rt) {
        sum[rt] = (sum[rt<<1] + sum[rt<<1|1]);
    }
    void build(int l, int r, int rt) {
        sum[rt] = (r - l + 1);
        if (l == r) {
            return ;
        }
        int mid = (l+r) >> 1;
        build(l, mid, rt<<1);
        build(mid+1, r, rt<<1|1);
    }
    int Query(int L, int l, int r, int rt) {
        sum[rt] --;
        if (l == r) {
            return l;
            cout << 'a' << l << endl;
        }
        int mid = (l + r) >> 1, ret = 0;
        if (sum[rt<<1] >= L) ret = Query(L, l, mid, rt<<1);
        else {
            L -= sum[rt<<1];
            ret = Query(L, mid+1, r, rt<<1|1);
        }
        return ret;
    }
    void Update(int p, int k, int l, int r, int rt) {
        if (l == r ) {
            sum[rt] --;
            return ;
        }
        int mid = (l + r) >> 1;
        if (p < mid) Update(p, k, l, mid, rt<<1);
        else Update(p, k, mid+1, r, rt<<1|1);
        PushUp(rt);
    }
    void input() {
        while (cin >>n) {
            memset(b, 0, sizeof(b));
            build(1, n, 1);
            for (int i = 0; i<n; i++) {
                scanf("%d%d", &a[i][0], &a[i][1]);
            }
            for (int i = n-1; i>=0; i--) {
                //cout << i << ' ' << a[i][0] << ' ' << a[i][1] << endl;
                b[Query(a[i][0]+1, 1, n, 1)] = a[i][1];
            }
            printf("%d", b[1]);
            for (int i = 2; i<=n ;i++) {
                printf(" %d", b[i]);
            }
            printf("\n");
        }
    }
};
int main() {
    SegTree S;
    S.input();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值