IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 线段树的一些基础应用 -> 正文阅读

[数据结构与算法]线段树的一些基础应用

线段树基础应用


线段树


luoguP3373 线段树2

1.题目大意:

给定一个源数组,要求你实现区间加,区间乘,区间查询的功能。

2.题目思路:

一眼线段树(名字也是如此)但与最基本的线段树区别在于,这里需要多实现一个区间乘的操作,看起来只是多了一个新功能,但由此引出了一系列的问题:
1.首先一定想到的是lazytag的push_down问题,显然这里lazy不能初始化为0而是为1再者lazy也需要乘来修改。
2.既然想到了lazy,那么有一个更复杂的问题是一定难以绕过的,在这颗线段树中我们有两个操作,分别是加和乘,而因为这两种操作的结果会互相影响,所以在传递lazy的时候也会有两种的规则,
第一加法优先即tr[p << 1]=((tr[p << 1] + lazya[p]) * lazym[p])%pmod这时会发现,修改加法的lazy时乘法会被连带修改成小数,不仅麻烦还会产生精度问题,这是我们不希望看到的,所以要舍弃。
第二乘法优先即tr[p << 1] = ((tr[p << 1] * lazym[p]) + lazya[p] * len) % mod这时如果乘法修改只要同时乘加法的lazy,而加法时就只用修改加法lazy,不会产生小数也不会有精度问题。
解决了上面的问题,这个线段树就和普通的线段树没有什么区别了。下面是代码.
#### AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAX = 1e5 + 10;

long long t[MAX << 2];
long long la[MAX << 2];
long long lam[MAX << 2];
int n, m, mod;

inline int lson(int x){
    return x << 1;
}

inline int rson(int x){
    return lson(x) | 1;
}

void push_up(int p){
    t[p] = (t[lson(p)] + t[rson(p)]) % mod;
}

void build(int p, int s, int e){//个人喜欢用这种方式建树,即节点只保存值而不保存区间左右界
    if(s == e){
        scanf("%lld", &t[p]);
        return;
    }
    int mid = s + ((e - s) >> 1);
    build(lson(p), s, mid);
    build(rson(p), mid + 1, e);
    push_up(p);
}

void push_down(int p, int s, int e){
    if(la[p] == 0 && lam[p] == 1){
        return;
    }
    long long& lazy = la[p];
    long long& lazym = lam[p];
    int mid = s + ((e - s) >> 1);
    int ls = lson(p);
    int rs = rson(p);
    t[ls] = (t[ls] * lazym + lazy * (mid - s + 1)) % mod;
    t[rs] = (t[rs] * lazym + lazy * (e - mid)) % mod;
    la[ls] = (la[ls] * lazym + lazy) % mod;
    la[rs] = (la[rs] * lazym + lazy) % mod;
    lam[ls] = (lam[ls] * lazym) % mod;
    lam[rs] = (lam[rs] * lazym) % mod;
    lazy = 0;
    lazym = 1;
}

long long query(int p, int s, int e, int l, int r){
    if(l <= s && e <= r){
        return t[p] % mod;
    }
    push_down(p, s, e);
    int mid = s + ((e - s) >> 1);
    long long ret = 0;
    if(l <= mid){
        ret = (ret + query(lson(p), s, mid, l, r)) % mod;
    }
    if(r > mid){
        ret = (ret + query(rson(p), mid + 1, e, l, r)) % mod;
    }
    return ret;
}


void modifya(int p, int s, int e, int l, int r, int d){
    if(l <= s && e <= r){
        t[p] = (t[p] + d * (e - s + 1)) % mod;
        la[p] = (la[p] + d) % mod;
        return;
    }
    push_down(p, s, e);
    int mid = s + ((e - s) >> 1);
    if(l <= mid){
        modifya(lson(p), s, mid, l, r, d);
    }
    if(r > mid){
        modifya(rson(p), mid + 1, e, l , r, d);
    }
    push_up(p);
}

void modifym(int p, int s, int e, int l, int r, int k){
    if(l <= s && e <= r){
        t[p] = (t[p] * k) % mod;
        la[p] = (la[p] * k) % mod;
        lam[p] = (lam[p] * k) % mod;
        return;
    }
    push_down(p, s, e);
    int mid = s + ((e - s) >> 1);
    if(l <= mid){
        modifym(lson(p), s, mid, l, r, k);
    }
    if(r > mid){
        modifym(rson(p), mid + 1, e, l, r, k);
    }
    push_up(p);
}

void slove(int j){
    if(1 == j){
        int l, r, k;
        scanf("%d%d%d", &l, &r, &k);
        modifym(1, 1, n, l, r, k);
    }
    else if(2 == j){
        int l, r, d;
        scanf("%d%d%d", &l, &r, &d);
        modifya(1, 1, n, l, r, d);
    }
    else if(3 == j){
        int l, r;
        scanf("%d%d", &l, &r);
        long long ans = query(1, 1, n, l, r);
        printf("%lld\n", ans);
    }
}

void Init(){
    build(1, 1, n);
    fill(lam, lam + n * 4 + 1, 1);
}

int main(){
    // freopen("out.txt","w",stdout);
    scanf("%d%d%d", &n, &m, &mod);
    Init();
    while(m--){
        int me = 0;
        scanf("%d", &me);
        slove(me);
    }
    return 0;
}

线段树区间合并


luoguP2894 Hotel G

1.题目大意:

现在有n ( 1 ? n ? 5 ? 1 0 4 ) (1 \leqslant n \leqslant 5 * 10^4) (1?n?5?104)个房间,有m个操作,接下来m行有先输入一个数。如果这个数为1,则再输入一个数x代表在1–n的区间内找到长度连续为x的空房,如果找到了就使其入住并输出左端点,如果找到多个则入住输出最小的。如果这个数为2,则在输入两个数xy代表将x–x+y+1的房间退房。

2.题目思路:

对于这个问题,因为涉及到区间修改问题,考虑用线段树完成,第二个操作的实现很简单,就是区间赋值为0,但第一个问题,普通的线段树似乎难以解决,因为线段树并不能知道区间内空房的具体情况,考虑这样一种情况,对于两个相邻的区间,左区间连续空房为m,右区间连续空房为n,但并不能直接判断父区间的值为两个子区间的大者,因为在合并时左区间右边与右区间左边可能产生新的连续空房,且大于m或n。
解决这个问题也很简单,就是对于每一个节点额外记录左右区间的连续空房量,并且在push_up和push_down的时候按照一定方法即可

void push_up(int x){
    if(tr[lson(x)].len == tr[lson(x)].num){
        tr[x].numl = tr[lson(x)].num + tr[rson(x)].numl;
    }
    else{
        tr[x].numl = tr[lson(x)].numl;
    }
    if(tr[rson(x)].len == tr[rson(x)].num){
        tr[x].numr = tr[lson(x)].numr + tr[rson(x)].num;
    }
    else{
        tr[x].numr = tr[rson(x)].numr;
    }
    tr[x].num = max(tr[lson(x)].num, max(tr[rson(x)].num, tr[lson(x)].numr + tr[rson(x)].numl));
}

push_up的写法

void push_down(int p, int s, int e){
    if(tr[p].lazy == 0) return;
    int& la = tr[p].lazy;
    if(1 == la){
        int son = lson(p);
        tr[son].num = tr[son].numl = tr[son].numr = 0;
        tr[son].lazy = 1;
        son = rson(p);
        tr[son].num = tr[son].numl = tr[son].numr = 0;
        tr[son].lazy = 1;
    } 
    if(2 == la){
        int son = lson(p);
        tr[son].num = tr[son].numl = tr[son].numr = tr[son].len;
        tr[son].lazy = 2;
        son = rson(p);
        tr[son].num = tr[son].numl = tr[son].numr = tr[son].len;
        tr[son].lazy = 2;
    }
    la = 0;
}

push_down的写法

3.AC代码:

#include <cstdio>
#include <iostream>
#include <assert.h>

using namespace std;

const int MAX = 5e4 + 10;

int n, m;

struct segt{
    int len;//节点代表区间的长度
    int num;//节点代表区间的最大连续空房数
    int numl;//以左端点为起点的连续空房数
    int numr;//以右端点为起点的连续空房数
    int lazy;//0代表无变化,1代表定房,2代表退房
} tr[MAX << 2];

inline int lson(int x){
    return x << 1;
}

inline int rson(int x){
    return lson(x) | 1;
}

void push_up(int x){
    if(tr[lson(x)].len == tr[lson(x)].num){
        tr[x].numl = tr[lson(x)].num + tr[rson(x)].numl;
    }
    else{
        tr[x].numl = tr[lson(x)].numl;
    }
    if(tr[rson(x)].len == tr[rson(x)].num){
        tr[x].numr = tr[lson(x)].numr + tr[rson(x)].num;
    }
    else{
        tr[x].numr = tr[rson(x)].numr;
    }
    tr[x].num = max(tr[lson(x)].num, max(tr[rson(x)].num, tr[lson(x)].numr + tr[rson(x)].numl));
}

void build(int p, int s, int e){
    tr[p].len = e - s + 1;
    if(s == e){
        tr[p].num = tr[p].numl = tr[p].numr = 1;
        tr[p].lazy = 0;
        return;
    }
    int mid = s + ((e - s) >> 1);
    build(lson(p), s, mid);
    build(rson(p), mid + 1, e);
    push_up(p);
}

void push_down(int p, int s, int e){
    if(tr[p].lazy == 0) return;
    int& la = tr[p].lazy;
    if(1 == la){
        int son = lson(p);
        tr[son].num = tr[son].numl = tr[son].numr = 0;
        tr[son].lazy = 1;
        son = rson(p);
        tr[son].num = tr[son].numl = tr[son].numr = 0;
        tr[son].lazy = 1;
    } 
    if(2 == la){
        int son = lson(p);
        tr[son].num = tr[son].numl = tr[son].numr = tr[son].len;
        tr[son].lazy = 2;
        son = rson(p);
        tr[son].num = tr[son].numl = tr[son].numr = tr[son].len;
        tr[son].lazy = 2;
    }
    la = 0;
}

int query(int p, int s, int e, int x){
    if(s == e) return s;
    int mid = s + ((e - s) >> 1);
    push_down(p, s, e);
    if(tr[lson(p)].num >= x) return query(lson(p), s, mid, x);
    if(tr[lson(p)].numr + tr[rson(p)].numl >= x) return mid - tr[lson(p)].numr + 1;
    if(tr[rson(p)].num >= x) return query(rson(p),mid + 1, e, x);
    return 0;
}

void checkin(int p, int s, int e, int l, int r){
    if(s >= l && e <= r){
        tr[p].num = tr[p].numl = tr[p].numr = 0;
        tr[p].lazy = 1;
        return;
    }
    push_down(p, s, e);
    int mid = s + ((e - s) >> 1);
    if(l <= mid) checkin(lson(p), s, mid, l, r);
    if(r > mid) checkin(rson(p), mid + 1, e, l, r);
    push_up(p);
}

void checkout(int p, int s, int e, int l, int r){
    if(s >= l && e <= r){
        // printf("p = %d s = %d e = %d l = %d r = %d\n",p, s, e, l, r);
        tr[p].num = tr[p].numl = tr[p].numr = tr[p].len;
        tr[p].lazy = 2;
        return;
    }
    push_down(p, s, e);
    int mid = s + ((e - s) >> 1);
    if(l <= mid) checkout(lson(p), s, mid, l, r);
    if(r > mid) checkout(rson(p), mid + 1, e, l, r);
    push_up(p);
}

int query_all(int p, int s, int e, int l, int r){
    if(s >= l && e <= r){
        return tr[p].num;
    }
    push_down(p, s, e);
    int ret = 0;
    int mid = s + ((e - s) >> 1);
    if(l <= mid) ret += query_all(lson(p), s, mid, l, r);
    if(r > mid) ret += query_all(rson(p), mid + 1, e, l, r);
    return ret;
}

int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n>>m;
    build(1, 1, n);
    for(int i = 1; i <= m; i++){
        int op;
        cin>>op;
        if(1 == op){
            int x;
            cin>>x;
            int l = query(1, 1, n, x);
            cout<<l<<endl;
            // cout<<query_all(1, 1, n, 2, 5)<<endl;
            if(l == 0) continue;
            checkin(1, 1, n, l, l + x - 1);
        }
        else if(2 == op){
            int x, y;
            cin>>x>>y;
            checkout(1, 1, n, x, x + y - 1);
            // cout<<query_all(1, 1, n, 3, 5)<<endl;
        }
        else{
            assert(op != 1 && op != 2);
        }
    }
    return 0;
}

权值线段树


线段树扫描线


luoguP5490(模板)

1. 题目大意:

给定n ( 1 ? n ? 1 0 5 ) (1 \leqslant n \leqslant 10^{5}) (1?n?105) 个矩形,以及n个矩形的四个顶点坐标 ( 0 ? x , y ? 1 0 9 ) (0 \leqslant x, y\leqslant 10^{9}) (0?x,y?109),求这n个矩形面积的并。

2. 题目思路:

1.首先想到暴力for求出,但看到数据范围显然MLE + TLE

2.再想到利用容斥定理先求出所有矩形面积之和再减去重叠部分,但过于麻烦。

那么应该怎么处理这个问题呢,可以想到分割图形求解,如同往一个盒子里灌水求体积一样,用一个数据结构来模拟逐渐上升的线,然后利用截线段长度乘上升的高度就可以得到部分的面积。显然,最佳的分割方式是每次遇到一条横边做为分割处。

3.考虑到矩形顶点可以到1e9则用离散化处理,此时问题变为了求某一段区间有线的长度(因为上升高度可以直接得出)随着截线的上升有线长度也会相应变化,既然问题涉及到了区间求解,则可以使用线段树来维护,以x轴作为区间,建一颗线段树,线段树节点保存节点对应的左右区间内被矩形覆盖的区域有多长。则问题可以得到解决。

AC代码:

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

#define int long long

int N;

const int MAX = 1e6 + 10;//需要开大一点,不然访问叶子节点的时候可能会re。

struct scan_line{//扫描线
    int l, r, h;//记录每一条横边的左右端点(长度)以及高度。
    int isdown;//记录是上边还是下边,上1下-1;
    bool operator < (const scan_line&rhs)const{
        return h < rhs.h;
    }
}line[MAX << 1];

int xx[MAX << 1];

struct segt{
    int l ,r;
    int cover;//cover代表这个节点代表的线段是不是被完全覆盖;
    int len; //len代表节点所包含覆盖覆盖区域的长度;
} t[MAX << 3];//最多有2n个区间,所以要开8倍大小;

int xx1, xx2, yy1, yy2;

inline int lson(int x){
    return x << 1;
}

inline int rson(int x){
    return lson(x) | 1;
}

void push_up(int x){
    int l = t[x].l, r = t[x].r;
    if(t[x].cover) t[x].len = xx[r + 1] - xx[l];
    else t[x].len = t[lson(x)].len + t[rson(x)].len;
    // cout<<t[x].len<<endl;
}

void build(int p, int s, int e){
    t[p].l = s, t[p].r = e;
    t[p].len = 0,t[p].cover = 0;
    if(s == e) return;
    int mid = s + ((e - s) >> 1);
    build(lson(p), s, mid);
    build(rson(p), mid + 1, e);
}

void up_loud(int p, int l, int r, int d){
    int s = t[p].l, e = t[p].r;
    if(xx[e + 1] <= l || xx[s] >= r) return;
    if(xx[e + 1] <= r && xx[s] >= l){
        t[p].cover += d;
        push_up(p);
        return;
    }
    // printf("hrhr\n");
    up_loud(lson(p), l, r, d);
    up_loud(rson(p), l, r, d);
    push_up(p);
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>N;
    for(int i = 1; i <= N; i++){
        cin>>xx1>>yy1>>xx2>>yy2;
        xx[(i << 1) - 1] = xx1, xx[i << 1] = xx2;
        line[(i << 1) - 1] = scan_line{xx1, xx2, yy1, 1};
        line[i << 1] = scan_line{xx1,xx2,yy2, -1};
    }
    sort(line + 1, line + (N << 1) + 1);
    sort(xx + 1, xx + (N << 1) + 1);
    // for(int i = 1; i <= (N << 1); i++){
    //     printf("x1 = %d x2 = %d, y = %d isdown = %d\n",line[i].l,line[i].r,line[i].h,line[i].isdown);
    // }
    int len = unique(xx + 1, xx + (N << 1) + 1) - xx - 1;
    build(1, 1, len - 1);
    long long ans = 0;
    for(int i = 1; i < (N << 1); i++){
        up_loud(1, line[i].l,line[i].r,line[i].isdown);
        ans += (line[i + 1].h - line[i].h) * t[1].len;
    }
    cout<<ans<<endl;
    return 0;
}

可持续化线段树


  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-09-24 10:50:25  更:2021-09-24 10:50:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 16:11:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码