1. 程式人生 > >【[NOI2016]區間】

【[NOI2016]區間】

發現自己的離散化姿勢一直有問題

今天終於掌握了正確的姿勢

雖然這並不能阻擋我noip退役爆零的歷史程序

還是先來看看離散化怎麼寫吧,我以前都是這麼寫的

for(std::set<int>::iterator it=s.begin();it!=s.end();it++)
    ma[*it]=++tot;

這是使用\(set+map\)的離散化,但是顯然巨大的常數是極大的劣勢

正確的操作應該是這個樣子

std::sort(a+1,a+n+1)
int tot=unique(a+1,a+n+1)-a-1;
for(re int i=1;i<=tot;i++)
    ma[a[i]]=i;

\(unique\)能將一個有序陣列去重,返回值是去重之後的尾地址,減去首地址就可以得到去重之後的數量了

之後再來看這道題,也是一道非常神的題

這道題告訴我們暴力通向錯誤的解,錯誤的解往往跟正解很接近了

首先\(O(n^2logn)\)還是比較好想的做法,我們先將所有的區間按照長度排序,之後我們定住一個區間作為長度最小的區間,強行套用線段樹暴力覆蓋之後的比它大的區間,直到這個區間有一個點被覆蓋了\(m\)次,那麼最後覆蓋上去的區間的長度減去定住的最小值就是答案了

對所有的答案取一個\(min\)就好了

這樣是顯然不行的呀,我們得想個辦法

於是我就想出來一種顯然錯誤的做法

我大膽的猜測答案是單調的

少年誰給你的勇氣

於是這個顯然錯誤的做法是這樣的,先將最短的區間一直覆蓋,直到覆蓋到符合條件為止,之後由於所謂的"答案單調",那麼使下一個次短的區間符合條件的區間一定在前面的答案的後面

這樣的複雜度均攤下來是對的

答案單調很有道理,但是涼涼了

很容易就能找到反例了

我們再覆蓋第一個最短的區間的時候,由於我們只是在判斷最短的區間是否滿足條件,所以可能這個時候之後的某個區間就突然滿足條件了,所以這個答案顯然不是單調的

但是這個錯誤的思路和正解已經非常接近了,差別只有一點,我們判斷的時候判斷的不是最短的區間是否符合條件,而是判斷整個區間內是否有點被覆蓋了\(m\)

這有什麼道理呢,其實聯想一下尺取法,和尺取法差不多

我們從最短的區間開始覆蓋,可能覆蓋的過程中滿足條件的點並沒有來自當前的區間,但是沒有關係,我們直接用當前的區間作為最小的區間就行了,這樣顯然只會導致答案偏大,而真正的最小區間我們在後面也會取到

程式碼

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
#include<map>
#define maxn 500005
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
std::set<int> s;
std::map<int,int> ma;
struct node
{
    int ll,rr,len;
    int L,R;
}a[maxn];
int b[maxn<<1];
int n,m,N;
int ans=19999999999;
int l[4000005],r[4000005],d[4000005],tag[4000005];
inline int read()
{
    char c=getchar();
    int x=0;
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9')
      x=(x<<3)+(x<<1)+c-48,c=getchar();
    return x;
}
inline int cmp(node K,node M)
{
    return K.len<M.len;
}
void build(int x,int y,int i)
{
    l[i]=x;
    r[i]=y;
    if(x==y) return;
    int mid=x+y>>1;
    build(x,mid,i<<1),build(mid+1,y,i<<1|1);
}
inline void pushdown(int i)
{
    if(!tag[i]) return;
    tag[i<<1]+=tag[i];
    tag[i<<1|1]+=tag[i];
    d[i<<1|1]+=tag[i];
    d[i<<1]+=tag[i];
    tag[i]=0;
}
void change(int x,int y,int v,int i)
{
    if(x<=l[i]&&y>=r[i])
    {
        d[i]+=v;
        tag[i]+=v;
        return;
    }
    pushdown(i);
    int mid=l[i]+r[i]>>1;
    if(y<=mid) change(x,y,v,i<<1);
    else if(x>mid) change(x,y,v,i<<1|1);
    else change(x,y,v,i<<1),change(x,y,v,i<<1|1);
    d[i]=max(d[i<<1],d[i<<1|1]);
}
int main()
{
    n=read();
    m=read();
    for(re int i=1;i<=n;i++)
        a[i].ll=read(),a[i].rr=read(),a[i].len=a[i].rr-a[i].ll,b[++N]=a[i].ll,b[++N]=a[i].rr;
    std::sort(a+1,a+n+1,cmp);
    std::sort(b+1,b+N+1);
    int tot=std::unique(b+1,b+N+1)-b-1;
    for(re int i=1;i<=tot;i++)
        ma[b[i]]=i;
    build(1,tot,1);
    for(re int i=1;i<=n;i++) a[i].L=ma[a[i].ll],a[i].R=ma[a[i].rr];
    int now=-1;
    for(re int i=1;i<=n;i++)
    {
        change(a[i].L,a[i].R,1,1);
        if(d[1]==m)
        {
            ans=a[i].len-a[1].len;
            now=i;
            break;
        }
    }
    if(now==-1)
    {
        puts("-1");
        return 0;
    }
    for(re int i=2;i<=n;i++)
    {
        change(a[i-1].L,a[i-1].R,-1,1);
        while(d[1]<m) 
        {
            if(now==n) 
            {
                printf("%d\n",ans);
                return 0;
            } 
            now++;
            change(a[now].L,a[now].R,1,1);
        }
        ans=min(ans,a[now].len-a[i].len);
    }
    printf("%d\n",ans);
    return 0;
}