BZOJ4653 [NOI2016] 区间 【线段树】
题目分析:
首先思考一个二分答案的做法。我们可以注意到答案具有单调性,所以可以二分答案。
假设当前二分的答案是$ k $。那么按照大小顺序插入每个区间,同时在末端删除会对答案产生影响的区间。这里不妨用线段树维护。这个做法在外国好像叫做two pointers。
如果某个时刻,线段树中有点大于等于$ m $,说明这个答案是合理的,可以向上二分。若全程没有点大于$ m $说明这个答案不合理,向下二分。
时间复杂度是$ O(nlognlogN) $的,会超时。
从这个方法入手,考虑如何优化。实际上,二分是不必要的。同样我们可以采用two pointers。如果某个时刻线段树中有点等于$ m $。我们完全可以从后端删除点。这是为什么呢?
理由是从后端删除掉的点与其它点产生的共鸣(姑且这么叫吧)肯定比当前的答案要大,若以删除对更好的答案是没有影响的。这样我们可以删除直到线段树中没有点等于$ m $。同时记录答案。
时间复杂度是$ O(nlogn) $的,可以通过所有数据。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 520000; 5 6 int n,m,Num; 7 8 int mp[maxn<<1]; 9 10 struct Dir{ 11 int l,r,len; 12 }d[maxn]; 13 14 class SegmentTree{ 15 private: 16 int lazy[maxn<<3],maxx[maxn<<3]; 17 void push_down(int now){ 18 maxx[now<<1] += lazy[now]; lazy[now<<1] += lazy[now]; 19 maxx[now<<1|1] += lazy[now]; lazy[now<<1|1] += lazy[now]; 20 lazy[now] = 0; 21 } 22 void push_up(int now){maxx[now] = max(maxx[now<<1],maxx[now<<1|1]);} 23 public: 24 void add(int now,int l,int r,int tl,int tr,int val){ 25 if(lazy[now] && tl != tr) push_down(now); 26 if(tl >= l && tr <= r){ 27 lazy[now] +=val; maxx[now]+=val; 28 return; 29 } 30 if(tl > r || tr < l) return; 31 int mid = (tl+tr)/2; 32 add(now<<1,l,r,tl,mid,val); 33 add(now<<1|1,l,r,mid+1,tr,val); 34 push_up(now); 35 } 36 int Query(){return maxx[1];} 37 }T; 38 39 int cmp(Dir a,Dir b){ 40 if(a.len<b.len) return true; 41 else return false; 42 } 43 44 void read(){ 45 scanf("%d%d",&n,&m); 46 for(int i=1;i<=n;i++){ 47 scanf("%d%d",&d[i].l,&d[i].r); d[i].len = d[i].r-d[i].l; 48 mp[++Num] = d[i].l; mp[++Num] = d[i].r; 49 } 50 sort(d+1,d+n+1,cmp); 51 sort(mp+1,mp+Num+1); 52 Num = unique(mp+1,mp+Num+1)-mp-1; 53 for(int i=1;i<=n;i++){ 54 d[i].l = lower_bound(mp+1,mp+Num+1,d[i].l)-mp; 55 d[i].r = lower_bound(mp+1,mp+Num+1,d[i].r)-mp; 56 } 57 } 58 59 void work(){ 60 int lastans = 2e9; 61 for(int i=1,j=1;i<=n;i++){ // two pointers 62 T.add(1,d[i].l,d[i].r,1,Num,1); 63 if(T.Query() == m){ 64 while(j <= i){ 65 T.add(1,d[j].l,d[j].r,1,Num,-1); 66 if(T.Query() != m){ 67 lastans = min(lastans,d[i].len-d[j].len); 68 j++; 69 break; 70 } 71 j++; 72 } 73 } 74 while(j <= i && d[i].len - d[j].len > lastans){ 75 T.add(1,d[j].l,d[j].r,1,Num,-1); 76 j++; 77 } 78 } 79 if(lastans == 2e9) puts("-1"); 80 else printf("%d",lastans); 81 } 82 83 int main(){ 84 read(); 85 work(); 86 return 0; 87 }