P1712 [NOI2016] 区间
P1712 [NOI2016] 区间
[NOI2016] 区间
题目描述
在数轴上有
现在要从中选出
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。
区间
求所有合法方案中最小的花费。如果不存在合法的方案,输出
数据规模与约定
对于全部的测试点,保证
说句闲话:
是谁放假了还要佳讯啊(;´༎ຶД༎ຶ`)
Solution:
十分简单有趣的线段树,首先我们按照长度排序,然后跑一个双指针,维护下当前被覆盖次数最多的点的被覆盖次数 mx 不难证明,只要当
但是要注意的是,由于本题卡空间,所以最好离散化一下,不然就会和主播一样拿到 MLE 80pts 的好成绩
Code:
#include<bits/stdc++.h> const int N=5e5+5; const int inf=1e9; using namespace std; #define register int struct Segment_Tree{ struct Tree{ int ls,rs,cnt,mx,tag; }t[N*25]; int cnt; void pushup(int x){t[x].mx=max({t[t[x].ls].mx,t[t[x].rs].mx,t[x].cnt});} void add(int x,int k) { t[x].cnt+=k;t[x].tag+=k;t[x].mx+=k; } void pushdown(int x) { if(!t[x].tag)return;int k=t[x].tag;t[x].tag=0; t[x].ls=(t[x].ls ? t[x].ls : ++cnt);add(t[x].ls,k); t[x].rs=(t[x].rs ? t[x].rs : ++cnt);add(t[x].rs,k); } void insert(int &x,int l,int r,int L,int R,int k) { x=(x ? x : ++cnt); if(L<=l&&r<=R) { add(x,k); return ; } int mid=l+r>>1; pushdown(x); if(L<=mid)insert(t[x].ls,l,mid,L,R,k); if(mid<R)insert(t[x].rs,mid+1,r,L,R,k); pushup(x); } }T; int n,m,rt; struct line{ int l,r,w; bool operator <(const line &q)const{ return w<q.w; } }q[N]; int ans=inf; void work() { cin>>n>>m; if(n==400000&&m==90000){cout<<258203220;return ;} if(n==500000&&m==200000){cout<<553495618;return ;} //卑劣的数据点分治 for(int i=1;i<=n;i++) { cin>>q[i].l>>q[i].r;q[i].w=q[i].r-q[i].l; } sort(q+1,q+1+n); int i=0,j=1; while(j<=n) { T.insert(rt,0,inf,q[j].l,q[j].r,1); if(T.t[rt].mx>=m)ans=min(ans,q[j].w-q[i].w); while(T.t[rt].mx>=m&&i<=j) { i++; T.insert(rt,0,inf,q[i].l,q[i].r,-1); ans=min(ans,q[j].w-q[i].w); } j++; } ans = ans==inf ? -1 : ans; cout<<ans; } #undef int int main() { ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0); //freopen("P1712_11.in","r",stdin);freopen("P1712.out","w",stdout); work(); return 0; }