bzoj4653: [Noi2016]区间
嗯这道题又涨姿势了。第一次看见这样的离散化。。666
说说做法吧,第一眼就看出线段树的说,贪心没看出来。。就只能膜网上的题解了,我们先将长度给排序一下,然后看看从当前位置(L)开始,要到哪个点(R)就可以达到m个区间共同包含至少一个位置,这就是一种解,判断m个区间的方式就是利用线段树维护一个区间最大值。对于那些没有贡献的区间,插入也不影响操作,因为L~R之间的不是最值,对答案没有影响。
#include<set> #include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int n,m; struct Seg { int l,r,lc,rc,c,u; }tr[2100000];int trlen; void bt(int l,int r) { trlen++;int now=trlen; tr[now].l=l;tr[now].r=r; tr[now].c=0;tr[now].u=0; tr[now].lc=tr[now].rc=-1; if(l<r) { int mid=(l+r)/2; tr[now].lc=trlen+1;bt(l,mid); tr[now].rc=trlen+1;bt(mid+1,r); } } void inherit(int now,int k) { tr[now].c+=k;tr[now].u+=k; } void change(int now,int l,int r,int k) { if(tr[now].l==l&&tr[now].r==r){inherit(now,k);return ;} int lc=tr[now].lc,rc=tr[now].rc; if(tr[now].u!=0) { inherit(lc,tr[now].u); inherit(rc,tr[now].u); tr[now].u=0; } int mid=(tr[now].l+tr[now].r)/2; if(r<=mid) change(lc,l,r,k); else if(mid+1<=l)change(rc,l,r,k); else { change(lc,l,mid,k); change(rc,mid+1,r,k); } tr[now].c=max(tr[lc].c,tr[rc].c); } struct lr { int l,r,s; }a[510000],b[510000]; int lslen,ls[1100000]; bool cmp(lr n1,lr n2) { if(n1.s<n2.s||(n1.s==n2.s&&n1.l<=n2.l))return true; return false; } void LSH() { lslen=0; for(int i=1;i<=n;i++) { ls[++lslen]=a[i].l; ls[++lslen]=a[i].r; } sort(ls+1,ls+1+lslen); lslen=unique(ls+1,ls+1+lslen)-ls-1; for(int i=1;i<=n;i++) { b[i].l=lower_bound(ls+1,ls+lslen+1,a[i].l)-ls; b[i].r=lower_bound(ls+1,ls+lslen+1,a[i].r)-ls; } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d%d",&a[i].l,&a[i].r); a[i].s=a[i].r-a[i].l; } sort(a+1,a+1+n,cmp); LSH(); trlen=0;bt(1,lslen); int L=1,R=0,ans=2147483647; while(L<=n) { while(R<n&&tr[1].c<m) { R++; change(1,b[R].l,b[R].r,1); } if(tr[1].c>=m)ans=min(ans,a[R].s-a[L].s); change(1,b[L].l,b[L].r,-1);L++; } if(ans==2147483647)printf("-1\n"); else printf("%d\n",ans); return 0; }
pain and happy in the cruel world.