【線段樹】[NOI2016]區間

題目描述

在數軸上有 n 個閉區間 [l1,r1],[l2,r2],...,[ln,rn] 。現在要從中選出 m 個區間,使得這 m 個區間共同包含至少一個位置。換句話說,就是使得存在一個 x ,使得對於每一個被選中的區間 [li,ri] ,都有 lixri

對於一個合法的選取方案,它的花費爲被選中的最長區間長度減去被選中的最短區間長度。區間 [li,ri] 的長度定義爲 rili ,即等於它的右端點的值減去左端點的值。

求所有合法方案中最小的花費。如果不存在合法的方案,輸出 1

輸入格式

第一行包含兩個正整數 n,m ,用空格隔開,意義如上文所述。保證 1mn

接下來 n 行,每行表示一個區間,包含用空格隔開的兩個整數 liri 爲該區間的左右端點。

輸出格式

只有一行,包含一個正整數,即最小花費。

樣例一

input

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

output

2

explanation

樣例圖

如圖,當 n=6, m=3 時,花費最小的方案是選取 [3,5][3,4][1,4] 這三個區間,他們共同包含了 4 這個位置,所以是合法的。其中最長的區間是 [1,4] ,最短的區間是 [3,4] ,所以它的花費是 (41)(43)=2

樣例二

見樣例數據下載。

樣例三

見樣例數據下載。

限制與約定

所有測試數據的範圍和特點如下表所示:

測試點編號 n m li,ri
12090liri100
210
319930liri100000
4200
510002
62000
7199600liri5000
820050
90liri109
1019995000liri5000
112000400
125000liri109
133000020000liri100000
14400001000
155000015000
1610000020000
172000000 \le l_i \le r_i \le 10^90liri109
1830000050000
1940000090000
20500000200000

時間限制:3s

空間限制:256MB

下載

樣例數據下載



分析

離散化,按照區間長度排序,維護一個雙指針,滑動窗口,用一個線段樹來維護點被覆蓋的次數即可。

代碼

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 500000
int r[MAXN*2+10],rcnt,m,n,ans=0x7fffffff;
struct itv{
    int l,r,len;
    inline itv(){
    }
    inline itv(int l,int r):l(l),r(r),len(r-l){
    }
    bool operator<(const itv &b)const{
        return len<b.len;
    }
}a[MAXN+10];
struct node{
    int tagp,tag0,mx;
}tree[MAXN*8+10];
inline void push_down(int i){
    if(tree[i].tag0){
        tree[i<<1].tag0=tree[(i<<1)|1].tag0=1;
        tree[i<<1].mx=tree[i<<1].tagp=0;
        tree[(i<<1)|1].mx=tree[(i<<1)|1].tagp=0;
        tree[i].tag0=0;
    }
    if(tree[i].tagp){
        tree[i<<1].mx+=tree[i].tagp,tree[(i<<1)].tagp+=tree[i].tagp;
        tree[(i<<1)|1].mx+=tree[i].tagp,tree[(i<<1)|1].tagp+=tree[i].tagp;
        tree[i].tagp=0;
    }
}
inline void update(int i){
    tree[i].mx=max(tree[i<<1].mx,tree[(i<<1)|1].mx);
}
void insert(int i,int l,int r,int ll,int rr,int d){
    if(ll<=l&&r<=rr){
        tree[i].tagp+=d;
        tree[i].mx+=d;
        return;
    }
    if(ll>r||rr<l)
        return;
    int mid((l+r)>>1);
    push_down(i);
    insert(i<<1,l,mid,ll,rr,d);
    insert((i<<1)|1,mid+1,r,ll,rr,d);
    update(i);
}
int get_mx(int i,int l,int r,int ll,int rr){
    if(ll<=l&&r<=rr)
        return tree[i].mx;
    if(ll>r||rr<l)
        return 0;
    int mid((l+r)>>1);
    push_down(i);
    return max(get_mx(i<<1,l,mid,ll,rr),get_mx((i<<1)|1,mid+1,r,ll,rr));
}
void Read(int &x){
    static char c;
    bool f(0);
    while(c=getchar(),c!=EOF){
        if(c=='-')
            f=1;
        else if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            if(f)
                x=-x;
            return;
        }
    }
}
void read(){
    Read(n),Read(m);
    int i,L,R;
    for(i=1;i<=n;i++){
        Read(L),Read(R);
        r[++rcnt]=L,r[++rcnt]=R;
        a[i]=itv(L,R);
    }
    sort(r+1,r+rcnt+1);
    rcnt=unique(r+1,r+rcnt+1)-r-1;
    for(i=1;i<=n;i++){
        a[i].l=lower_bound(r+1,r+rcnt+1,a[i].l)-r;
        a[i].r=lower_bound(r+1,r+rcnt+1,a[i].r)-r;
    }
    sort(a+1,a+n+1);
}
void solve(){
    int i=1,j=2;
    tree[1].tag0=1,tree[1].tagp=tree[1].mx=0;
    insert(1,1,rcnt,a[1].l,a[1].r,1);
    while(i<=n){
        while(j<=n&&tree[1].mx<m){
            insert(1,1,rcnt,a[j].l,a[j].r,1);
            j++;
        }
        if(tree[1].mx<m)
            break;
        ans=min(ans,a[j-1].len-a[i].len);
        insert(1,1,rcnt,a[i].l,a[i].r,-1);
        i++;
    }
}
int main()
{
    read();
    solve();
    if(ans==0x7fffffff)
        puts("-1");
    else
        printf("%d\n",ans);
}
發佈了171 篇原創文章 · 獲贊 64 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章