P1712 [NOI2016]区间
P1712 [NOI2016]区间
题意:
给你n个线段让你找出\(m\)个线段且这个\(m\)个线段都有一个公共点\(x\),对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。问合法选取的方案花费最小是?
题解:
由于数据太大,首先肯定能想到离散化。
想到离散化就会想到线段树去维护区间信息,对与区间(l, r)就在线段是将区间(l, r)的信息加1就行了。
但是问题来了。
如何才能找到m个线段都有公共点且花费最小呢?
如果让所有线段都从小答案排序, 那么答案是不是一定在这个排序后里面连续的一段?
如果不明白就仔细想一想。
如果知道了这个。那不就简单了。
用线段树维护,每个简单被覆盖的次数, 如果 \(tree[1] >= m\)说明有一个点的覆盖次数大于m,也就说明有m个线段相交一点, 那怎么找到m个线段花费呢?
我们用个\(last\) 初始为1, 我首先开始向线段树里面插入线段。
当 \(tree[1] >= m\)
就移动\(last\) 直到 \(tree[1] < m\) 那么答案就是 当前线段的长度减去 \(last - 1\)的那个线段的长度
然后继续插入线段 重复上述操作。
为啥这样就一定能得到答案, 因为线段是按长度从小到达排序, 自己想一想就明白了。有点向滑动窗口
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;
int n, sum;
int tree[10 * N];
vector<int> g;
#define m (l + r) / 2
#define lson 2 * node
#define rson 2 * node + 1
int add[4 * N];
struct line {
int x, y;
} lin[N];
bool cmp(line x, line y) {
return (x.y - x.x) < (y.y - y.x);
}
int get_id(int x) {
return lower_bound(g.begin(), g.end(), x) - g.begin() + 1;
}
void push_down(int node) {
if (add[node]) {
tree[lson] += add[node];
tree[rson] += add[node];
add[lson] += add[node];
add[rson] += add[node];
add[node] = 0;
}
}
void update(int v, int ql, int qr, int l, int r, int node) {
if (ql <= l && qr >= r) {
tree[node] += v;
add[node] += v;
return;
}
push_down(node);
if (ql <= m) update(v, ql, qr, l, m, lson);
if (qr > m) update(v, ql, qr, m + 1, r, rson);
tree[node] = max(tree[lson], tree[rson]);
}
int main() {
scanf("%d %d", &n, &sum);
for (int i = 1; i <= n; i++) {
int x, y;
scanf("%d %d", &x, &y);
g.push_back(x);
g.push_back(y);
lin[i].x = x, lin[i].y = y;
}
sort(g.begin(), g.end());
g.erase(unique(g.begin(), g.end()), g.end());
sort(lin + 1, lin + n + 1, cmp);
int last = 1;
int ans = INT_MAX;
for (int i = 1; i <= n; i++) {
int l = get_id(lin[i].x);
int r = get_id(lin[i].y);
update(1, l, r, 1, g.size(), 1);
if (tree[1] == sum) {
while (tree[1]>= sum) {
l = get_id(lin[last].x);
r = get_id(lin[last].y);
update(-1, l, r, 1, g.size(), 1);
last++;
}
ans = min(ans, (lin[i].y - lin[i].x) - (lin[last - 1].y - lin[last - 1].x));
}
}
if (ans == INT_MAX) {
cout << -1 << endl;
} else {
cout << ans << endl;
}
}