前言
在往常的题目中,线段树的叶子节点是代表一个点的情况;
而我这次遇到了一个题目,需要维护线段之间的关系;
因此我们将线段树的每个节点都看成一个线段;
思路
我们定义线段树的某个节点维护区间 [ L , R ] [L,R] [L,R],表示从 L 到 R + 1 的 一 条 线 段 L到R+1的一条线段 L到R+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 L和R转变为 L 和 R − 1 L和R-1 L和R−1对线段树进行操作;
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;
}