Address
Solution
- 考虑怎样快速动态地判断一组方案是否合法。
- 显然可以用线段树维护,先对所有区间离散化,加入区间
[li,ri]
时就把该区间内每个位置都加上 1,删去就是减去 1,并维护区间的最大值。
- 记线段树根节点维护的最大值为
num
,则当
num=m
时,方案合法。
- 进一步我们注意到,一组方案的花费只跟最长区间长度和最短区间长度有关。
- 那么对于一组
m
个区间的合法方案,我们已知它的最长和最短区间长度,那么加入一些区间长度在最短到最长之间的区间,显然对花费没有影响。
- 换句话说,如果我们把区间按照长度从大到小排序,任意取出其中一段并加入线段树中,若这时
num≥m
,则这段的所有区间可作为一种合法方案。
- 并且这时因为已经排好序了,计算答案只要用最左边的区间长度减去最右边的区间长度。
- 于是我们就可以用 尺取法 来计算最优的花费了。
- 先把所有区间排好序,记录两个指针
i,j
表示合法方案选取的区间
[j,i]
:
- 每次把指针
i
右移,将区间
[li,ri]
加入线段树中。
- 若此时
num≥m
,计算花费更新答案,之后删去区间
[lj,rj]
,并将指针
j
右移,重复这一步过程直到
num<m
。
-
i,j
的右移显然是
O(n)
的,加上线段树上每次
O(logn)
的操作,总复杂度
O(nlogn)
。
Code
#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
using namespace std;
namespace inout
{
const int S = 1 << 20;
char frd[S], *ihed = frd + S;
const char *ital = ihed;
inline char inChar()
{
if (ihed == ital)
fread(frd, 1, S, stdin), ihed = frd;
return *ihed++;
}
inline int get()
{
char ch; int res = 0; bool flag = false;
while (!isdigit(ch = inChar()) && ch != '-');
(ch == '-' ? flag = true : res = ch ^ 48);
while (isdigit(ch = inChar()))
res = res * 10 + ch - 48;
return flag ? -res : res;
}
char fwt[S], *ohed = fwt;
const char *otal = ohed + S;
inline void outChar(char ch)
{
if (ohed == otal)
fwrite(fwt, 1, S, stdout), ohed = fwt;
*ohed++ = ch;
}
inline void put(int x)
{
if (x > 9) put(x / 10);
outChar(x % 10 + 48);
}
};
using namespace inout;
const int Maxn = 0x3f3f3f3f;
const int N = 5e5 + 5, M = N << 3;
int tag[M], len[M], sum[M], b[N << 1];
int n, m, q, Ans = Maxn;
struct point
{
int l, r, z;
inline void scan()
{
l = get(); r = get(); z = r - l;
}
inline bool operator < (const point &x) const
{
return z > x.z;
}
}p[N];
#define sL s << 1
#define sR s << 1 | 1
inline int Max(int x, int y) {return x > y ? x : y;}
inline void Uptdate(int s)
{
sum[s] = Max(sum[sL], sum[sR]);
}
inline void addTag(int s, int v)
{
tag[s] += v; sum[s] += v;
}
inline void pushDown(int s)
{
if (tag[s])
{
addTag(sL, tag[s]);
addTag(sR, tag[s]);
tag[s] = 0;
}
}
inline void Modify(int s, int l, int r, int x, int y, int v)
{
if (l == x && r == y) return addTag(s, v);
pushDown(s); int mid = l + r >> 1;
if (y <= mid)
Modify(sL, l, mid, x, y, v);
else if (x > mid)
Modify(sR, mid + 1, r, x, y, v);
else
{
Modify(sL, l, mid, x, mid, v);
Modify(sR, mid + 1, r, mid + 1, y, v);
}
Uptdate(s);
}
inline void Build(int s, int l, int r)
{
len[s] = r - l + 1;
if (l == r) return ;
int mid = l + r >> 1;
Build(sL, l, mid); Build(sR, mid + 1, r);
}
inline void CkMin(int &x, int y) {if (x > y) x = y;}
int main()
{
n = get(); q = get();
for (int i = 1; i <= n; ++i)
p[i].scan(), b[++m] = p[i].l, b[++m] = p[i].r;
sort(b + 1, b + m + 1);
m = unique(b + 1, b + m + 1) - b - 1;
for (int i = 1; i <= n; ++i)
{
p[i].l = lower_bound(b + 1, b + m + 1, p[i].l) - b;
p[i].r = lower_bound(b + 1, b + m + 1, p[i].r) - b;
}
sort(p + 1, p + n + 1);
Build(1, 1, m);
for (int i = 1, j = 1; i <= n; ++i)
{
Modify(1, 1, m, p[i].l, p[i].r, 1);
while (j <= i && sum[1] >= q)
{
CkMin(Ans, p[j].z - p[i].z);
Modify(1, 1, m, p[j].l, p[j].r, -1);
++j;
}
}
printf("%d\n", Ans == Maxn ? -1 : Ans);
return 0;
}