1. 程式人生 > >洛谷1712 BZOJ4653 NOI2016 區間 線段樹 離散化

洛谷1712 BZOJ4653 NOI2016 區間 線段樹 離散化

題目連結 題意: 在數軸上有NN個閉區間[l1,r1],[l2,r2],...,[ln,rn][l_1,r_1],[l_2,r_2],...,[l_n,r_n]。現在要從中選出MM個區間,使得這MM個區間共同包含至少一個位置。換句話說,就是使得存在一個xx,使得對於每一個被選中的區間[li,ri][l_i,r_i],都有lixril_i≤x≤r_i。 對於一個合法的選取方案,它的花費為被選中的最長區間長度減去被選中的最短區間長度。區間[li,ri][l_i,r_i]

]的長度定義為rilir_i-l_i,即等於它的右端點的值減去左端點的值。 求所有合法方案中最小的花費。如果不存在合法的方案,輸出-1 。

題解: 我們先對所有的l和r離散化,對每一個區間按照離散化之前區間長度為關鍵字從小到大排序,我們答案要我們插入的所有區間中最大的和最小的儘可能接近,所有我們從小到大插入區間,看插入到什麼時候出現一個點被覆蓋了m次。我們會發現,對於lll+1l+1,他們對應的右端點一定是單調不降的,原因是你不可能之前llrr的最大值恰好第一次到達mm,但是從l+1l+1到一個比rr小的xx也能到達mm,那樣與rr是第一個到達m

m的點矛盾了。於是我們可以用two pointer的方法,維護當前用於覆蓋的區間是[l,r][l,r]。對於一個ll,找到第一個能使被覆蓋mm次的點,第一個點保證了最大的區間儘可能小;然後嘗試右移左端點ll,使得這個下限盡可能大,於是只有這樣相減得出的答案可能是最優解。而我們發現每次右端點右移加入一個新的區間相當於對於離散化後的區間進行區間加,然後左端點右移是去掉某個區間的影響,相當於區間減,然後判斷是否有被覆蓋mm次的點相當於區間求最大值,於是就可以用線段樹維護了。

程式碼:

#include <bits/stdc++.h>
using namespace std;
int n,m,b[1000010],cnt,l,r,ans=2e9; struct node { int l,r,del; }a[500010]; struct tree { int l,r,tag,mx; }tr[4000010]; inline int cmp(node x,node y) { return x.del<y.del; } inline void pushup(int rt) { tr[rt].mx=max(tr[rt<<1].mx,tr[rt<<1|1].mx); } inline void build(int rt,int l,int r) { tr[rt].l=l; tr[rt].r=r; tr[rt].mx=0; tr[rt].tag=0; if(l==r) return; int mid=(l+r)>>1; build(rt<<1,l,mid); build(rt<<1|1,mid+1,r); } inline void pushdown(int rt) { if(tr[rt].tag!=0) { tr[rt<<1].mx+=tr[rt].tag; tr[rt<<1|1].mx+=tr[rt].tag; tr[rt<<1].tag+=tr[rt].tag; tr[rt<<1|1].tag+=tr[rt].tag; tr[rt].tag=0; } } inline void update(int rt,int le,int ri,int x) { int l=tr[rt].l,r=tr[rt].r; if(le<=l&&r<=ri) { tr[rt].mx+=x; tr[rt].tag+=x; return; } pushdown(rt); int mid=(l+r)>>1; if(le<=mid) update(rt<<1,le,ri,x); if(mid+1<=ri) update(rt<<1|1,le,ri,x); pushup(rt); } inline int query(int rt,int le,int ri) { int l=tr[rt].l,r=tr[rt].r; if(le<=l&&r<=ri) return tr[rt].mx; pushdown(rt); int mid=(l+r)>>1,res=0; if(le<=mid) res=max(res,query(rt<<1,le,ri)); if(mid+1<=ri) res=max(res,query(rt<<1|1,le,ri)); return res; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { scanf("%d%d",&a[i].l,&a[i].r); b[++cnt]=a[i].l; b[++cnt]=a[i].r; a[i].del=a[i].r-a[i].l; } sort(b+1,b+cnt+1); cnt=unique(b+1,b+cnt+1)-b-1; for(int i=1;i<=n;++i) { a[i].l=lower_bound(b+1,b+cnt+1,a[i].l)-b; a[i].r=lower_bound(b+1,b+cnt+1,a[i].r)-b; } sort(a+1,a+n+1,cmp); build(1,1,cnt); l=1; r=1; while(l<=n&&r<=n) { while(r<=n&&query(1,1,cnt)<m) { update(1,a[r].l,a[r].r,1); ++r; } if(query(1,1,cnt)<m||r>n) break; while(query(1,1,cnt)>=m&&l<=n) { update(1,a[l].l,a[l].r,-1); ++l; } ans=min(ans,a[r-1].del-a[l-1].del); } if(ans<2e9) printf("%d\n",ans); else printf("-1\n"); return 0; }