1. 程式人生 > >[NOI2016]區間-線段樹

[NOI2016]區間-線段樹

一道很巧妙的題。

首先我們需要解決的問題,怎麼快速判斷選出的m個區間是否存在交。

我們反過來考慮這個問題,

我們每一個選出的區間,就對應的線上段樹上區間加1,那麼只要存在最大值等於m,就一定有m個區間滿足條件了。

那麼把區間從小到大排序,一直加到最大值等於m,更新答案,然後刪掉最小區間,不停的做下去就好了。

#include <bits/stdc++.h>
#define lson p<<1
#define rson (p<<1)|1
using namespace std;
inline int gi () {
    int
x=0, w=0; char ch=0; while (! (ch>='0' && ch<='9') ) { if (ch=='-') w=1; ch=getchar (); } while (ch>='0' && ch<='9') { x= (x<<3) + (x<<1) + (ch^48); ch=getchar (); } return w?-x:x; } const int N=5e5+10; int n,m,head=1,Ans=0x3f3f3f3f
,Num,Ls[N<<1],LS[N<<1],Cnt[N<<3],Tag[N<<3]; struct Sect { int l, r, val; }s[N]; bool Cmp (Sect a, Sect b) { return a.val<b.val; } void Pushup (int p) { Cnt[p]=max (Cnt[lson], Cnt[rson]); } void Pushdown (int p) { if (!Tag[p]) return; Tag[lson]+=Tag[p]; Tag[rson]
+=Tag[p]; Cnt[lson]+=Tag[p]; Cnt[rson]+=Tag[p]; Tag[p]=0; } void Modify (int p, int l, int r, int Ml, int Mr, int val) { if (l>=Ml && Mr>=r) { Cnt[p]+=val; Tag[p]+=val; return; } Pushdown (p); int Mid= (l+r) >> 1; if (Ml<=Mid) Modify (lson, l, Mid, Ml, Mr, val); if (Mr>Mid) Modify (rson, Mid+1, r, Ml, Mr, val); Pushup (p); } int main () { n=gi (), m=gi (); memset (Ls, -1, sizeof (Ls) ); memset (LS, -1, sizeof (LS) ); for (int i=1;i<=n;++i) { s[i].l=gi (), s[i].r=gi (); s[i].val=s[i].r-s[i].l; Ls[++Num]=s[i].l, Ls[++Num]=s[i].r; } sort (Ls+1, Ls+Num+1); for (int i=1, j=0;i<=Num;++i) { if (Ls[i]!=Ls[i-1]) LS[++j]=Ls[i]; if (i==Num) { Num=j; break; } } for (int i=1;i<=n;++i) { s[i].l=lower_bound (LS+1, LS+Num+1, s[i].l) -LS; s[i].r=lower_bound (LS+1, LS+Num+1, s[i].r) -LS; } sort (s+1, s+n+1, Cmp); for (int i=1;i<=n;++i) { Modify (1, 1, Num, s[i].l, s[i].r, 1); while (Cnt[1]==m) { Ans=min (Ans, s[i].val-s[head].val); Modify (1, 1, Num, s[head].l, s[head].r, -1); head++; } } printf ("%d\n", Ans==0x3f3f3f3f?-1:Ans); return 0; }
BY BHLLX