1. 程式人生 > 實用技巧 >【NOI2016】區間 題解(線段樹+尺取法)

【NOI2016】區間 題解(線段樹+尺取法)

題目連結

題目大意:給定$n$個區間$[l_i,r_i]$,選出$m$個區間使它們有一個共同的位置$x$,且使它們產生的費用最小。求最小費用。費用定義為最長的區間長度減去最短區間長度。

-----------------

因為區間順序改動又不影響答案,我們不妨按照長度排個序。看到資料範圍果斷離散化。

思考一種最樸素的做法:將排好序的區間逐一加入數軸,看有沒有一個點被覆蓋的次數$\geq m$。

有的話就統計一下答案,然後將前面加入的數刪掉,直到$<m$。

顯然用到了尺取法。維護是否有一個點被覆蓋的次數可以用線段樹。

然後就可以成功A掉此題。

程式碼:

#include<bits/stdc++.h>
using
namespace std; int n,m,maxx,b[1000005],cnt,size,head=0,tail=0,ans=0x3f3f3f3f; struct node { int l,r,len; }a[500005]; struct tree { int index,l,r,max,lazy; }tree[4000005]; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10
+ch-'0';ch=getchar();} return x*f; } bool cmp(node x,node y) { return x.len<y.len; } inline void build(int index,int l,int r) { tree[index].l=l; tree[index].r=r; if (l==r) return; int mid=(l+r)>>1; build(index*2,l,mid); build(index*2+1,mid+1,r); } inline void pushdown(int
index) { tree[index*2].lazy+=tree[index].lazy; tree[index*2+1].lazy+=tree[index].lazy; tree[index*2+1].max+=tree[index].lazy; tree[index*2].max+=tree[index].lazy; tree[index].lazy=0; } inline void update(int index,int l,int r,int x) { if (l<=tree[index].l&&tree[index].r<=r) { tree[index].max+=x; tree[index].lazy+=x; return; } if (tree[index].lazy) pushdown(index); int mid=(tree[index].l+tree[index].r)>>1; if (l<=mid) update(index*2,l,r,x); if (r>mid) update(index*2+1,l,r,x); tree[index].max=max(tree[index*2+1].max,tree[index*2].max); } int main() { n=read(),m=read(); for (int i=1;i<=n;i++) { a[i].l=read(),a[i].r=read();a[i].len=a[i].r-a[i].l; b[++cnt]=a[i].l;b[++cnt]=a[i].r; } sort(b+1,b+cnt+1); size=unique(b+1,b+cnt+1)-b-1; sort(a+1,a+n+1,cmp); for (int i=1;i<=n;i++) { a[i].l=lower_bound(b+1,b+size+1,a[i].l)-b; a[i].r=lower_bound(b+1,b+size+1,a[i].r)-b; maxx=max(maxx,a[i].r); } build(1,1,maxx); while(tail<n) { while(tree[1].max<m&&tail<=n){tail++;update(1,a[tail].l,a[tail].r,1);} if (tree[1].max<m) break; while(head<=tail&&tree[1].max>=m) { head++;update(1,a[head].l,a[head].r,-1); ans=min(ans,a[tail].len-a[head].len); } } if (ans==0x3f3f3f3f) printf("-1"); else printf("%d",ans); return 0; }