题目链接:https://codeforces.com/contest/1555/problem/E
题目大致意思是:现给出n条线段,给出每条线段的起点终点,和花费。现在要求选出任意个的线段,使之能铺满数轴1~m之间的所有线段(就是说数轴要被选出的所有线段完全覆盖),并且此选出的线段集的花费极差要尽量小,求这个最小值。从题目中我们就能看出一个性质,如果我们已然确定最大值线段和最小值线段的话,那么在花费在最大值最小值之间的所有线段随便取也不会对结果产生影响,那我们不如一股脑全塞进去,反正也没影响嘛。建立一颗线段树维护区间最小值,记录数轴[l,r]区间内所有子线段最少被覆盖几次。如果[1,m]区间内最小覆盖次数大于0,那我们就可以确定区间内所有点都至少有被覆盖。所以我们先按照花费排序,固定左端点,让右端点尽量靠近左端点。更新答案,左端点向右移动一位,再去寻找下一个右端点。上代码
#include <bits/stdc++.h>
using namespace std;
int tree1[2000050<<2],tree2[2000050<<2];
struct Seg{
int l,r,w;
}s[1000005];
bool cmp(Seg x,Seg y){
return x.w<y.w;
}
void push(int v){
tree1[v<<1|1]+=tree1[v],tree1[v<<1]+=tree1[v];
tree2[v<<1|1]+=tree1[v],tree2[v<<1]+=tree1[v];
tree1[v]=0;
}
void add(int l,int r,int L,int R,int rt,int val){
push(rt);
//if(l==r&&r==4) cout<<rt<<endl;
if(l>=L&&r<=R){tree1[rt]+=val;tree2[rt]+=val;push(rt);return;}
int mid=(l+r)>>1;
if(L<=mid) add(l,mid,L,R,rt<<1,val);
if(R>mid) add(mid+1,r,L,R,rt<<1|1,val);
tree2[rt]=min(tree2[rt<<1|1],tree2[rt<<1]);
//cout<<rt<<" "<<tree2[rt]<<endl;
//cout<<l<<" "<<r<<" "<<L<<" "<<R<<" "<<tree2[rt]<<" "<<tree2[rt<<1]<<" "<<tree2[rt<<1|1]<<endl;
}
int n,m;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>s[i].l>>s[i].r>>s[i].w;
}
int ans=2147483647;
sort(s+1,s+1+n,cmp);
int j=1;
for(int i=1;i<=n;i++){
while(j<=n&&tree2[1]==0){
add(1,m-1,s[j].l,s[j].r-1,1,1);
//cout<<i<<" "<<j<<" "<<tree2[1]<<endl;
j++;
}
//cout<<i<<" "<<j-1<<endl;
if(tree2[1]==0) break;
ans=min(ans,s[j-1].w-s[i].w);
add(1,m-1,s[i].l,s[i].r-1,1,-1);
}
cout<<ans;
//for(int i=1;i<=4;i++) add(1,m-1,s[i].l,s[i].r-1,1,1);
//add(1,m-1,s[1].l,s[1].r-1,1,-1);
//cout<<tree2[1];
//add(1,11,10,11,1,1);
//add(1,11,11,11,1,1);
//add(1,11,1,4,1,1);
//add(1,11,4,9,1,1);
//cout<<tree2[1];
/*add(1,5,1,5,1,1);
cout<<tree2[1]<<endl;
add(1,5,1,3,1,-1);
cout<<tree2[1]<<endl;*/
return 0;
}
Comments | NOTHING