在數軸上有 n個閉區間 [l1,r1],[l2,r2],...,[ln,rn]。現在要從中選出 m 個區間,使得這 m個區間共同包含至少乙個位置。換句話說,就是使得存在乙個 x,使得對於每乙個被選中的區間 [li,ri],都有 li≤x≤ri。
對於乙個合法的選取方案,它的花費為被選中的最長區間長度減去被選中的最短區間長度。區間 [li,ri] 的長度定義為 ri−li,即等於它的右端點的值減去左端點的值。
求所有合法方案中最小的花費。如果不存在合法的方案,輸出 −1。
第一行包含兩個正整數 n,m用空格隔開,意義如上文所述。保證 1≤m≤n
接下來 n行,每行表示乙個區間,包含用空格隔開的兩個整數 li 和 ri 為該區間的左右端點。
n<=500000,m<=200000,0≤li≤ri≤10^9
只有一行,包含乙個正整數,即最小花費。
6 33 5
1 23 4
2 21 5
1 4比較容易想到,如果我們對所選的區間都+1,那麼一定存在某個位置的值》=m
先對區間離散化
這樣我們可以得到乙個\(o(n^3)\)的演算法
區間修改求最值,離散化後用線段樹優化,可以做到\(o(n^2logn)\)
為了進一步減小乙個n,我們將區間按區間長度排序
從小開始逐個加入線段樹中,直到出現》=m的位置,再嘗試從小開始從線段樹中刪除,直至滿足》=m時再移動右端點
可以證明這樣做的正確性
因為區間過多不影響存在某位置》=m,通過不斷移動右端點,有解一定可以判出
設當前解為len,如果不移動左端點,右端點移動的結果一定不會比len要小
所以在滿足條件的情況下應盡量移動左端點
\(o(nlogn)\)
#include#include#include#include#include#define ll long long int
#define rep(i,n) for (int i = 1; i <= (n); i++)
#define redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define bug(s,n) for (int i = 1; i <= (n); i++) cout<57)
while (c >= 48 && c <= 57)
return out * flag;
}struct nodee[maxm];
int b[maxn],bi,tot = 1,n,m,ans = inf;
int mx[4 * maxn],tag[4 * maxn];
inline bool operator <(const node& a,const node& x)
int getn(int x)
void pd(int u)
}void modify(int u,int l,int r,int l,int r,int x)
pd(u);
int mid = l + r >> 1;
if (mid >= l) modify(ls,l,mid,l,r,x);
if (mid < r) modify(rs,mid + 1,r,l,r,x);
mx[u] = max(mx[ls],mx[rs]);
}int main()
r++;
if (r > n) break;
r = b[e[r].r] - b[e[r].l];
modify(1,1,tot,e[r].l,e[r].r,1);
if (mx[1] >= m) ans = min(ans,r - l);
} printf("%d\n",ans == inf ? -1 : ans);
return 0;
}
線段樹 NOI2016 區間
在數軸上有 n 個閉區間 l 1,r1 l 2,r2 l n,rn 現在要從中選出 m 個區間,使得這 m個區間共同包含至少乙個位置。換句話說,就是使得存在乙個 x 使得對於每乙個被選中的區間 l i,ri 都有 li x r i 對於乙個合法的選取方案,它的花費為被選中的最長區間長度減去被選中的最...
NOI2016 區間 線段樹
將區間按照長度排序,每次將相同長度的區間插入,直到有乙個點能被至少m個區間覆蓋,這個可以用線段樹維護。這時我們就可以刪除長度小的區間直到不滿足條件。重複做就可以了 1 include 2 include 3 include 4 include 5 include 6 define ll long l...
NOI 2016 區間(雙指標 線段樹)
noi 2016 區間 首先離散化,然後對所有區間按照區間長度排序。然後用雙指標。雙指標保持 l r 這段的所有區間疊加後存在疊加次數為 m 的點。然後 an s等於每一次合法時的最小值。這樣顯然是對的。因為乙個合法的答案一定在某次雙指標區間內計數。include define lson rt 1 ...