NOI 2016 区间

洛谷 P1712 [NOI2016]区间

题目传送门

题目描述

在数轴上有NN 个闭区间 [l_1,r_1],[l_2,r_2],...,[l_n,r_n][l1,r1],[l2,r2],...,[l**n,r**n] 。现在要从中选出MM 个区间,使得这MM 个区间共同包含至少一个位置。换句话说,就是使得存在一个 xx ,使得对于每一个被选中的区间[l_i,r_i][l**i,r**i] ,都有 l_i≤x≤r_il**ixr**i

对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间[l_i,r_i][l**i,r**i] 的长度定义为r_i-l_ir**il**i ,即等于它的右端点的值减去左端点的值。

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

输入格式

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

接下来NN 行,每行表示一个区间,包含用空格隔开的两个整数l_il**i 和r_ir**i 为该区间的左右端点。

N<=500000,M<=200000,0≤li≤ri≤10^9N<=500000,M<=200000,0≤l**ir**i≤109

输出格式

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

输入输出样例

输入 #1复制

输出 #1复制

说明/提示

img

img

题解:

(第二道紫题)

(开心)

这是一道线段树的题,加了一点点的尺取法思想(为此我特意学了尺取法)

所以我们得出这样的思路,先按照区间长度大小排序,进行离散化,按照我们排过序后的顺序依次加入区间,然后看一下是否有一个点的区间被覆盖的次数大于等于m,如果有的话,我们还需要统计最小长度,就尝试着把前面的内容依次删除,知道<m(临界点)。

其实这就是一个尺取法的过程。

通过上述的思路,我们发现这个思路代码实现上的难点一个是离散化,一个是尺取,还有一个就是如何统计得出有一个点的被覆盖次数比m大。

显然暴力枚举是不行的,所以我们考虑线段树维护这个覆盖次数。

所以就可以了。

代码大致如下:

#include<cstdio>
#include<algorithm>
#define lson pos<<1
#define rson pos<<1|1
using namespace std;
const int maxn=500001;
const int INF=1e9;
struct Point
{
    int val,id;
}p[maxn<<2];
struct Data
{
    int len,id;
}a[maxn<<2];
int L[maxn<<1],R[maxn<<1],n,m,maxr,cnt,num;
int tree[maxn<<3],add[maxn<<3];
bool cmp1(Point a,Point b)
{
    return a.val<b.val;
}
bool cmp2(Data a,Data b)
{
    return a.len<b.len;
}
void pushdown(int pos,int l,int r)
{
    if(!add[pos])
        return;
    tree[lson]+=add[pos];
    tree[rson]+=add[pos];
    add[lson]+=add[pos];
    add[rson]+=add[pos];
    add[pos]=0;
    return;
}
void fix(int pos,int l,int r,int x,int y,int val)
{
    if(x>r||y<l)
        return;
    if(x<=l&&y>=r)
    {
        tree[pos]+=val;
        add[pos]+=val;
        return;
    }
    int mid=(l+r)/2;
    pushdown(pos,l,r);
    fix(lson,l,mid,x,y,val);
    fix(rson,mid+1,r,x,y,val);
    tree[pos]=max(tree[lson],tree[rson]);
    return;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        a[i].len=v-u;
        a[i].id=i;
        p[++cnt].val=u;
        p[cnt].id=i;
        p[++cnt].val=v;
        p[cnt].id=i;
    }
    p[0].val=-1;
    sort(p+1,p+cnt+1,cmp1);
    for(int i=1;i<=cnt;i++)
    {
        if(p[i].val!=p[i-1].val)
            num++;
        int order=p[i].id;
        if(!L[order])
            L[order]=num;
        else 
            R[order]=num;
    }
    maxr=num;
    sort(a+1,a+n+1,cmp2);
    int ans=INF,le=0,ri=0;
    while(1)
    {
        while(tree[1]<m && ri<=n)
        {
            ri++;
            int u=a[ri].id,v=L[u],w=R[u];
            fix(1,1,maxr,v,w,1);
        }
        if(tree[1]<m)
            break;
        while(tree[1]>=m && le<=n)
        {
            le++;
            int u=a[le].id,v=L[u],w=R[u];
            fix(1,1,maxr,v,w,-1);
        }
        ans=min(ans,a[ri].len-a[le].len);
    }
    if(ans==INF)
        printf("-1");
    else
        printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/11314125.html