BZOJ 4653: [Noi2016]区间 离散化+线段树+单调指针维护

title

BZOJ 4653

LUOGU 1712

Description

在数轴上有 \(N\) 个闭区间 \([l_1,r_1],[l_2,r_2],...,[l_n,r_n]\)。现在要从中选出 \(M\) 个区间,使得这 \(M\) 个区间共同包含至少一个位置。换句话说,就是使得存在一个 \(x\),使得对于每一个被选中的区间 \([l_i,r_i]\),都有 \(l_i\leqslant x\leqslant r_i\)

对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 \([l_i,r_i]\) 的长度定义为 \(r_i−l_i\),即等于它的右端点的值减去左端点的值。

求所有合法方案中最小的花费。如果不存在合法的方案,输出 \(−1\)

Input

第一行包含两个正整数 \(N,M\) 用空格隔开,意义如上文所述。保证 \(1\leqslant M\leqslant N\)

接下来 \(N\) 行,每行表示一个区间,包含用空格隔开的两个整数 \(l_i\)\(r_i\) 为该区间的左右端点。

\(N\leqslant 500000,M\leqslant 200000,0\leqslant l_i\leqslant r_i\leqslant 10^9\)

Output

只有一行,包含一个正整数,即最小花费。

Sample Input

6 3
3 5
1 2
3 4
2 2
1 5
1 4

Sample Output

2

analysis

有一点再次碰上网络流24题的感觉,但是细看一下,根本不一样(略过这段话)。

又来堂而皇之地考虑暴力怎么写了,肯定要把所有区间排序一下,然后枚举一个区间作为左端点,一个区间作为右端点,那么答案就是在这个大区间中来选的。

这里有一个思维转化:

\(M\) 个区间共同包含一个位置,就相当于这一段区间内有被经过 \(M\) 次的点。

所以做法就出来了,我们把这一段的所有区间的对应的一段的经过次数都加一,最后只需查看一下这一段中是否出现了一个被经过 \(M\) 次的点,一旦存在就说明,我们一定可以找到其中的 \(M\) 个区间满足题目的要求,就可以用右端点的那个区间长度-左端点的那个区间长度来更新答案。

然后用线段树来维护,复杂度是 \(O(N^2\log N)\)

不难(很难)发现:右端点其实不单调不降的,所以没有必要枚举右端点了,只需要用一个单调指针一直往后扫就可以了,复杂度就变成了 \(O(N\log N)\)

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10,inf=0x7fffffff;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}

char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x)
{
    if (!x) *fe++=48;
    if (x<0) *fe++='-', x=-x;
    T num=0, ch[20];
    while (x) ch[++num]=x%10+48, x/=10;
    while (num) *fe++=ch[num--];
    *fe++='\n';
}

int tree[maxn<<2],atag[maxn<<2];//由于一个区间有两个点,所以最坏情况要开八倍数组,maxn已经乘2了
inline void pushdown(int now,int l,int r)
{
    if (l==r) { atag[now]=0; return ; }
    atag[now<<1]+=atag[now], atag[now<<1|1]+=atag[now];
    tree[now<<1]+=atag[now], tree[now<<1|1]+=atag[now];
    atag[now]=0;
}

inline void Change(int now,int l,int r,int tl,int tr,int k)
{
    if (tl<=l && r<=tr)
    {
        tree[now]+=k, atag[now]+=k;
        return ;
    }
    if (atag[now]) pushdown(now,l,r);
    int mid=(l+r)>>1;
    if (tl<=mid) Change(now<<1,l,mid,tl,tr,k);
    if (tr>mid) Change(now<<1|1,mid+1,r,tl,tr,k);
    tree[now]=max(tree[now<<1],tree[now<<1|1]);
}

struct Orz{int l,r,i;}q[maxn];
inline bool cmp(Orz a,Orz b)
{
    return a.i<b.i;
}

int c[maxn<<1],cnt;
int main()
{
    int n,m;read(n);read(m);
    for (int i=1; i<=n; ++i)
    {
        read(q[i].l),read(q[i].r);
        q[i].i=q[i].r-q[i].l;
        c[++cnt]=q[i].l,c[++cnt]=q[i].r;
    }
    sort(c+1,c+cnt+1);
    cnt=unique(c+1,c+cnt+1)-c-1;
    for (int i=1; i<=n; ++i) q[i].l=lower_bound(c+1,c+cnt+1,q[i].l)-c,q[i].r=lower_bound(c+1,c+cnt+1,q[i].r)-c;//找到区间i可修改的范围
    sort(q+1,q+n+1,cmp);
    int now=0,ans=inf;
    for (int i=1; i<=n; ++i)
    {
        while (tree[1]<m && now<n) ++now,Change(1,1,cnt,q[now].l,q[now].r,1);
        if (tree[1]==m) ans=min(ans,q[now].i-q[i].i);
        Change(1,1,cnt,q[i].l,q[i].r,-1);
    }
    write(ans==inf?-1:ans);
    flush();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/G-hsm/p/11350545.html
今日推荐