[NOI2016] 区间
题目描述
在数轴上有 n n n 个闭区间从 1 1 1 至 n n n 编号,第 i i i 个闭区间为 [ l i , r i ] [l_i,r_i] [li,ri] 。
现在要从中选出 m m m 个区间,使得这 m m m 个区间共同包含至少一个位置。换句话说,就是使得存在一个 x x x ,使得对于每一个被选中的区间 [ l i , r i ] [l_i,r_i] [li,ri],都有 l i ≤ x ≤ r i l_i \leq x \leq r_i li≤x≤ri 。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。
区间 [ l i , r i ] [l_i,r_i] [li,ri] 的长度定义为 ( r i − l i ) (r_i-l_i) (ri−li) ,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 − 1 -1 −1。
对于全部的测试点,保证 1 ≤ m ≤ n 1 \leq m \leq n 1≤m≤n, 1 ≤ n ≤ 5 × 1 0 5 1 \leq n \leq 5 \times 10^5 1≤n≤5×105, 1 ≤ m ≤ 2 × 1 0 5 1 \leq m \leq 2 \times 10^5 1≤m≤2×105, 0 ≤ l i ≤ r i ≤ 1 0 9 0 \leq l_i \leq r_i \leq 10^9 0≤li≤ri≤109。
解析:
将所有区间按长度排序,则最优解一定是连续的几个区间
如果区间 i i i 是最短的区间,设 R i R_i Ri 是满足条件的区间中最短的那个,此时答案为 l e n ( R i ) − l e n ( i ) len(R_i)-len(i) len(Ri)−len(i)
可以发现, R i R_i Ri 单调不减,可以用双指针
所以依次选择排序后的区间,如果满足某个点覆盖次数大于等于m,则将最早加入的区间删除,直到小于m
用线段树维护区间中点覆盖次数的最大值,需要离散化
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e6+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
struct sgt{
int v, tag;
}t[maxn << 2];
struct node{
int l, r;
int len;
bool operator < (const node &b) const{
return len < b.len;
}
}a[maxn];
int res = INF;
int x[maxn<<1], tot;
int n, m;
int ls(int x){return x << 1;}
int rs(int x){return x << 1 | 1;}
void pushup(int k){
t[k].v = max(t[ls(k)].v, t[rs(k)].v);
}
void build(int k, int l, int r){
t[k].tag = 0;
if(l == r){
t[k].v = 0;
return;
}
int mid = (l+r) >> 1;
build(ls(k), l, mid);
build(rs(k), mid+1, r);
pushup(k);
}
void pushdown(int k){
if(t[k].tag){
t[ls(k)].v += t[k].tag;
t[ls(k)].tag += t[k].tag;
t[rs(k)].v += t[k].tag;
t[rs(k)].tag += t[k].tag;
t[k].tag = 0;
}
}
void modify(int k, int l, int r, int x, int y, int w){
if(x <= l && y >= r){
t[k].tag += w;
t[k].v += w;
return;
}
int mid = (l+r) >> 1;
pushdown(k);
if(x <= mid)
modify(ls(k), l, mid, x, y, w);
if(y > mid)
modify(rs(k), mid+1, r, x, y, w);
pushup(k);
}
int query(int k, int l, int r, int x, int y){
if(x <= l && y >= r)
return t[k].v;
pushdown(k);
int mid = (l+r) >> 1;
int res = 0;
if(x <= mid)
res = max(res, query(ls(k), l, mid, x, y));
if(y > mid)
res = max(res, query(rs(k), mid+1, r, x, y));
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++){
cin >> a[i].l >> a[i].r;
a[i].len = (a[i].r - a[i].l);
x[++tot] = a[i].l;
x[++tot] = a[i].r;
}
sort(x+1, x+tot+1);
tot = unique(x+1, x+tot+1) - (x+1);
sort(a+1, a+1+n);
for(int i = 1; i <= n; i++){
a[i].l = lower_bound(x+1, x+tot+1, a[i].l) - x;
a[i].r = lower_bound(x+1, x+tot+1, a[i].r) - x;
}
build(1, 1, tot);
int pos = 1;
for(int i = 1; i <= n; i++){
modify(1, 1, tot, a[i].l, a[i].r, 1);
while(query(1, 1, tot, 1, tot) >= m){
res = min(res, a[i].len - a[pos].len);
modify(1, 1, tot, a[pos].l, a[pos].r, -1);
pos++;
}
}
if(res == INF)
cout << -1 << endl;
else
cout << res << endl;
return 0;
}