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 }

 

posted @ 2018-06-01 20:13  menhera  阅读(141)  评论(0编辑  收藏  举报