线段树叶子节点代表线段

前言

在往常的题目中,线段树的叶子节点是代表一个点的情况;

而我这次遇到了一个题目,需要维护线段之间的关系;

因此我们将线段树的每个节点都看成一个线段;

思路

我们定义线段树的某个节点维护区间 [ L , R ] [L,R] [L,R],表示从 L 到 R + 1 的 一 条 线 段 L到R+1的一条线段 LR+1线

比如线段树节点维护区间 [ 3 , 3 ] [3,3] [3,3]表示从点3持续到点4的一条线段;

例题

2021ICPC网络赛D题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
题意就是给我们若干个三元组 [ L , R , W ] [L,R,W] [L,R,W],表示覆盖区间[L,R]的代价为W;

要我们求一个最小代价;

思路

按权值排序,从大到小遍历;

权值大的被权值小的覆盖必然是赚的;

全部覆盖完以后,暴力查询每个节点即可;

因为我们定义线段树上的点 [ L , R ] [L,R] [L,R]映射的是区间 [ L , R + 1 ] [L,R+1] [L,R+1]

因此我们区间覆盖的时候,需要将题目给的 L 和 R L和R LR转变为 L 和 R − 1 L和R-1 LR1对线段树进行操作;

Code

#include<bits/stdc++.h>

using namespace std;

int t,n,m;

typedef long long ll;

const int N = 1e5+10;

struct Node{
    int l,r,w;
}a[N];
struct Tree{
    int l,r,w;
    bool cover;
}tr[N<<2];
#define lc (p<<1)
#define rc (p<<1|1)

void build(int p,int l,int r){
    tr[p].l = l,tr[p].r = r,tr[p].cover = false;
    tr[p].w = 0;
    if(l == r){
        return;
    }
    int mid = (l+r) >> 1;
    build(lc,l,mid);
    build(rc,mid+1,r);
}

void push_down(int p){
    if(tr[p].cover){
        tr[lc].cover = tr[rc].cover = 1;
        tr[lc].w = tr[rc].w = tr[p].w;
        tr[p].cover = 0;
    }
}
int query_num(int p,int l,int r){
    if(tr[p].cover){
        return (tr[p].r-tr[p].l+1)*tr[p].w;
    }
    //如果到底了 且没有覆盖
    if(tr[p].l == tr[p].r) return 0;
    int mid = (tr[p].l + tr[p].r) >> 1;
    if(r<=mid) return query_num(lc,l,r);
    else if(l>mid) return query_num(rc,l,r);
    else return query_num(lc,l,mid) + query_num(rc,mid+1,r);
}
void range_cover(int p,int l,int r,int w){
    if(tr[p].l >=l && tr[p].r <= r){
        tr[p].cover = 1;
        tr[p].w = w;
        return;
    }
    push_down(p);
    int mid = (tr[p].l + tr[p].r) >> 1;
    if(r<=mid) range_cover(lc,l,r,w);
    else if(l>mid) range_cover(rc,l,r,w);
    else{
        range_cover(lc,l,mid,w);
        range_cover(rc,mid+1,r,w);
    }
}
ll sum,ans;
int read(){
    int x=0;bool f=0;char c=getchar();
    while (c<'0'||c>'9'){if (c=='-')f=1;c=getchar();}
    while (c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}

inline void write(int x)
{
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
bool check(){
    sort(a+1,a+1+m,[](Node p,Node q)->bool{
                return p.l < q.l;
             });
    bool ok = 1;
    int R = a[1].r,L = a[1].l;
    for(int i=2;i<=m;++i){
        if(a[i].l > R){
            ok = false;
            break;
        }
        R = max(R,a[i].r);
        L = min(L,a[i].l);
    }
    if(R != n || L != 1) ok = false;
    return ok;
}
int main(){
    t = read();
    int c = 0;
    while(t--){
        ++c;
        sum = 0;
        ans = 0;
        n=read(),m=read();
        for(int i=1,l,r,w;i<=m;++i){
            l=read(),r=read(),w=read();
            a[i].l = l,a[i].r=r,a[i].w=w;
            sum += ((1ll*w*(r-l+1)*(r-l))/2);
        }
        bool ok = check();
        if(!ok){
            printf("Case #%d: Gotta prepare a lesson",c);
            if(t != 0) putchar('\n');
            continue;
        }
        sort(a+1,a+1+m,[](Node p,Node q)->bool{
                return p.w < q.w;
            });
        build(1,1,n);
        for(int i=m,l,r,w;i>=1;--i){
            l = a[i].l,r = a[i].r,w=a[i].w;
                        //如果是定义节点表示[L+1,R]
                        //那么range_cover写(1,l+1,r,w)
                        //我们定义的是[L,R+1] 应该写成range_cover(1,l,r-1)
            range_cover(1,l,r-1,w);
        }
        ans = query_num(1,1,n);
        printf("Case #%d: %lld",c,sum-ans);
        if(t != 0) putchar('\n');
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值