【题解】NOI2016区间

时间:2022-09-25 16:52:26

  Two - pointer 第一题…… 大概就是对于一段连续的区间求解,使用两个指针不断卡区间的长度直到区间不满足条件吧。

  这题只要对区间以长度从小到大排一下序,然后使用两个指针指向区间。线段树维护被覆盖最多次数的节点被覆盖了多少次。如果满足条件,由于我们是在第一次判断的时候发现它满足条件的,所以最后加入的这个区间一定对于答案产生了贡献,也就是最大的区间。要使最小区间最大化,我们只需让前面的指针慢慢往后跳直到不符合条件即可。

#include <bits/stdc++.h>
using namespace std;
#define maxn 5000000
#define INF 999999999
int n, m, tot, cnt, b[maxn];
int ans = INF, num[maxn];
int mark[maxn];
map <int, int> Map; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} struct node
{
int l, r, len;
friend bool operator <(const node& a, const node& b)
{
if(a.len != b.len) return a.len < b.len;
else if(a.l != b.l) return a.l < b.l;
else return a.r < b.r;
}
}A[maxn]; void push_down(int p)
{
if(!mark[p]) return;
num[p << ] += mark[p], num[p << | ] += mark[p];
mark[p << ] += mark[p];
mark[p << | ] += mark[p];
mark[p] = ;
} void update(int p, int l, int r, int L, int R, int x)
{
if(L <= l && R >= r)
{
num[p] += x; mark[p] += x;
return;
}
if(L > r || R < l) return;
push_down(p);
int mid = (l + r) >> ;
update(p << , l, mid, L, R, x), update(p << | , mid + , r, L, R, x);
num[p] = max(num[p << ], num[p << | ]);
} int main()
{
n = read(), m = read();
for(int i = ; i <= n; i ++)
{
A[i].l = read(), A[i].r = read(), A[i].len = A[i].r - A[i].l;
b[++ tot] = A[i].l, b[++ tot] = A[i].r;
}
sort(A + , A + + n);
sort(b + , b + + tot); b[] = -;
for(int i = ; i <= tot; i ++)
if(b[i] != b[i - ]) Map[b[i]] = ++ cnt;
sort(A + , A + + n);
for(int i = ; i <= n; i ++) A[i].l = Map[A[i].l], A[i].r = Map[A[i].r];
for(int i = , j = ; i <= n; i ++)
{
update(, , cnt, A[i].l, A[i].r, );
if(num[] < m) continue;
int tem = A[i].len, tem2 = ;
while(num[] >= m)
{
tem2 = A[j].len;
update(, , cnt, A[j].l, A[j].r, -), j ++;
}
ans = min(ans, tem - tem2);
}
if(ans == INF) printf("-1\n");
else printf("%d\n", ans);
return ;
}