考虑将这
n
n
n个区间按照长度从大到小排序,在选择区间的时候显然是选择连续的一段,即使中间某一个区间可能并没有什么用处,但是至少不会使答案变得更劣。
那么我们枚举最左边选取的是哪一个区间,然后扫到下一个正好使得所有数出现次数最多的那个出现了不少于
m
m
m次,然后更新答案。
类似于two-pointers的做法就可以实现上述过程。
维护每一个数出现了多少次我们直接离散化初始区间然后线段树实现即可。
时间复杂度:
O
(
n
log
n
)
O(n\log n)
O(nlogn)
代码
#include<bits/stdc++.h>#define N 1000010usingnamespace std;template<typename T>voidchkmax(T &x, T y){x = x > y ? x : y;}template<typename T>voidchkmin(T &x, T y){x = x > y ? y : x;}template<typename T>voidread(T &x){
x =0;int f =1;char c =getchar();while(!isdigit(c)){if(c =='-') f =-1; c =getchar();}while(isdigit(c)) x = x *10+ c -'0', c =getchar(); x *= f;}struct Node {int l, r;booloperator<(const Node &a)const{return r - l > a.r - a.l;}} a[N];struct SegmentTree {int mx[N *4], del[N *4];#define lc k << 1#define rc k << 1 | 1voidupdate(int k){mx[k]=max(mx[lc], mx[rc]);}voidpushdown(int k){int x = del[k]; del[k]=0;
mx[lc]+= x, mx[rc]+= x;
del[lc]+= x, del[rc]+= x;}voidmodify(int k,int l,int r,int L,int R,int v){if(L <= l && r <= R)return mx[k]+= v, del[k]+= v,void();if(del[k])pushdown(k);int mid =(l + r)>>1;if(L <= mid)modify(lc, l, mid, L, R, v);if(R > mid)modify(rc, mid +1, r, L, R, v);update(k);}intquery(){return mx[1];}} T;int b[N];intmain(){int n, m, len =0;read(n),read(m);for(int i =1; i <= n; i++)read(a[i].l),read(a[i].r), b[++len]= a[i].l, b[++len]= a[i].r;sort(a +1, a + n +1);sort(b +1, b + len +1); len =unique(b +1, b + len +1)- b -1;for(int i =1; i <= n; i++){
a[i].l =lower_bound(b +1, b + len +1, a[i].l)- b;
a[i].r =lower_bound(b +1, b + len +1, a[i].r)- b;}int l =0, ans = INT_MAX;for(int i =1; i <= n; i++){while(l < n && T.query()< m)++l, T.modify(1,1, len, a[l].l, a[l].r,1);if(T.query()>= m)chkmin(ans,(b[a[i].r]- b[a[i].l])-(b[a[l].r]- b[a[l].l]));
T.modify(1,1, len, a[i].l, a[i].r,-1);}if(ans == INT_MAX) cout <<"-1\n";else cout << ans <<"\n";return0;}