(线段树 + 尺取法)[NOI2016] 区间
题目链接:
题目描述:
在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 −1
输入描述:
第一行包含两个正整数 n,m用空格隔开,意义如上文所述。保证 1≤m≤n
接下来 n行,每行表示一个区间,包含用空格隔开的两个整数 li 和 ri 为该区间的左右端点。
N<=500000,M<=200000,0≤li≤ri≤10^9
输出描述:
只有一行,包含一个正整数,即最小花费。
输入
6 3
3 5
1 2
3 4
2 2
1 5
1 4
输出
2
思路:
我认为这道题的难点就是如何找到当一个x满足条件时,如何去算出覆盖在x上最长区间与最短区间之差,这个可以将每段区间按照长度
排序,(从大到小,从小到大都可以),我用的是从大到小,再用尺取法当满足条件时,对答案进行一次更新,最后的答案就是我们所求。
AcCode:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int N = 5e5 + 10, M = 2e5 + 10;
int n, m;
struct Segment{
int l, r;
int len;
}seg[N];
struct Node{
int l, r;
int cnt;
int add;
}tr[N << 3];
vector<int> v;
bool cmp(Segment &x, Segment &y){return x.len > y.len;}
void pushup(int u){
tr[u].cnt = max(tr[u << 1].cnt, tr[u << 1 | 1].cnt);
}
void pushdown(int u){
if(tr[u].add){
tr[u<<1].cnt += tr[u].add;
tr[u<<1|1].cnt += tr[u].add;
tr[u<<1].add += tr[u].add;
tr[u<<1|1].add += tr[u].add;
tr[u].add = 0;
}
}
int find(int y){
return lower_bound(v.begin(), v.end(), y) - v.begin() + 1;
}
void build(int u, int l, int r){
tr[u].l = l, tr[u].r = r;
if(l == r) return ;
int mid = l + r >> 1;
build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
}
void modify(int u, int l, int r, int k){
if(tr[u].l >= l && tr[u].r <= r){
//cout << tr[u].l << " " << tr[u].r << endl;
tr[u].cnt += k;
tr[u].add += k;
}
else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u << 1, l, r, k);
if(r > mid) modify(u << 1 | 1, l, r, k);
pushup(u);
}
}
int eval(int i, int j){return seg[i].r - seg[i].l - seg[j].r + seg[j].l;}
int main(void){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++){
cin >> seg[i].l >> seg[i].r;
seg[i].len = seg[i].r - seg[i].l;
v.push_back(seg[i].l), v.push_back(seg[i].r);
}
sort(v.begin(), v.end());
sort(seg + 1, seg + n + 1, cmp);
v.erase(unique(v.begin(), v.end()), v.end());
build(1, 1, n * 2);
int res = inf;
// for(int i = 1; i <= n; i++)
// cout << find(seg[i].l) << " " << find(seg[i].r) << endl;
// cout << "--------------" << endl;
for(int i = 1, j = 0; i <= n; i++){
//cout << tr[1].cnt << endl;
while(j < n && tr[1].cnt < m)
j ++, modify(1, find(seg[j].l), find(seg[j].r), 1);
if(j == n || tr[1].cnt < m) break;
//cout << j << endl;
modify(1, find(seg[i].l), find(seg[i].r), -1);
res = min(res, eval(i, j));
}
//cout << tr[1].cnt << endl;
if(res == inf) cout << -1 << endl;
else cout << res << endl;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战