Codeforces Round #765 Div.1 F. Souvenirs 线段树

题目链接:http://codeforces.com/contest/765/problem/F

 

题意概述:

给出一个序列,若干组询问,问给出下标区间中两数作差的最小绝对值。

 

分析:

这个题揭示着数据结构题目世界的真谛......

在线显然凉凉......考虑离线做法。

 

本题的主要思想:

  首先考虑把所有的询问按照右端点排序,用一个指针扫整个序列,同时考虑i作为一组差的右端可以对右端点大于等于i的询问做出的贡献。我们可以发现,我们扫到当前点i,对于所有询问的右端点大于等于i的询问,如果其左端点小于等于i,那么i这个位置就可能对它们的答案做出贡献,换句话说我们只要考虑i作为差的右端可以对哪些左端的答案做出贡献,然后维护当前每个点作为左端的答案,查询的时候直接进行区间最小值的查询即可。

 

  绝对值是麻烦的,因此我们简单一点,考虑所有j<i并且aj>=ai的贡献,然后倒着扫一遍就可以考虑剩下的可能贡献。

  假设我们现在扫到位置i,左边有第一个大于ai的元素aj,我们贪心考虑,最终能够让 i作为下标较大的一方参与的答案变得更小 的j'满足:ai<=aj'<aj。但是这个性质显然还不够强,我们继续考虑,发现:

  当aj'-ai>aj-aj'时,我们反向扫回来的时候,由于区间[j',j]一定被[j',i]包含,如果存在一个询问包括了[j',i],那么也一定包括了[j',j],显然这个时候aj-aj'是一个更加优秀的答案,aj'-ai更劣。

  因此我们找到的j'一定要满足:aj'-ai<=aj-aj' -> 2aj'<=ai+aj,也就是说我们最多找到O(log(ai+a_first_j))个可以对i作为右端点的答案产生贡献的位置。

  因为我们将右端点排序,因此我们维护当前所有的点作为下标较小的一方参与的时候的答案,当我们遇到一个询问p的右端点pr=i的时候,只需要查询区间[pl,i]中的最小值即可。

  最后倒着来一遍,得到最终的答案。

  在log次查找的时候我们实质上找的是权值在[ai,(ai+aj)/2]范围中的小于j的最大下标,分析一下发现可以直接用一棵权值线段树维护,因为我们找的权值区间内的序列下标不可能大于j(不然的话我们在之前就应该找到它了)。

  找到第一个j的时候可以直接在序列线段树中进行二分。

 

  时间复杂度O(NlogNloga)

  (PS:突然发现变量rank,hash在C++11下面编译不了是什么情况?!)

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<queue>
  8 #include<set>
  9 #include<map>
 10 #include<vector>
 11 #include<cctype>
 12 #define inf (1e9+5)
 13 using namespace std;
 14 const int MAXN=100005;
 15 const int MAXM=300005;
 16 
 17 int N,M,A[MAXN],Rank[MAXN],B[MAXN],ans[MAXM],tot;
 18 struct data{
 19     int l,r,id;
 20     friend bool operator < (data x,data y){
 21         return x.r<y.r;
 22     }
 23 }q[MAXM];
 24 struct segment_tree{
 25     static const int maxn=200005;
 26     int rt,np,lc[maxn],rc[maxn],mx[maxn],mn[maxn];
 27     void init(){ rt=np=0; }
 28     void pushup1(int now){ mn[now]=min(mn[lc[now]],mn[rc[now]]); }
 29     void pushup2(int now){ mx[now]=max(mx[lc[now]],mx[rc[now]]); }
 30     void build(int &now,int L,int R){
 31         now=++np,lc[now]=rc[now]=0,mx[now]=-1,mn[now]=inf;
 32         if(L==R) return;
 33         int m=L+R>>1;
 34         build(lc[now],L,m);
 35         build(rc[now],m+1,R);
 36     }
 37     void update1(int now,int L,int R,int pos,int v){
 38         if(L==R){ mn[now]=min(mn[now],v); return; }
 39         int m=L+R>>1;
 40         if(pos<=m) update1(lc[now],L,m,pos,v);
 41         else update1(rc[now],m+1,R,pos,v);
 42         pushup1(now);
 43     }
 44     void update2(int now,int L,int R,int pos,int v){
 45         if(L==R){ mx[now]=max(mx[now],v); return; }
 46         int m=L+R>>1;
 47         if(pos<=m) update2(lc[now],L,m,pos,v);
 48         else update2(rc[now],m+1,R,pos,v);
 49         pushup2(now);
 50     }
 51     int query_p(int now,int L,int R,int v){
 52         if(L==R) return mx[now]>=v?L:-1;
 53         int m=L+R>>1;
 54         if(mx[rc[now]]>=v) return query_p(rc[now],m+1,R,v);
 55         return query_p(lc[now],L,m,v);
 56     }
 57     int query_max(int now,int L,int R,int A,int B){
 58         if(A<=L&&R<=B) return mx[now];
 59         int m=L+R>>1;
 60         if(B<=m) return query_max(lc[now],L,m,A,B);
 61         if(A>m) return query_max(rc[now],m+1,R,A,B);
 62         return max(query_max(lc[now],L,m,A,B),query_max(rc[now],m+1,R,A,B));
 63     }
 64     int query_min(int now,int L,int R,int A,int B){
 65         if(A<=L&&R<=B) return mn[now];
 66         int m=L+R>>1;
 67         if(B<=m) return query_min(lc[now],L,m,A,B);
 68         if(A>m) return query_min(rc[now],m+1,R,A,B);
 69         return min(query_min(lc[now],L,m,A,B),query_min(rc[now],m+1,R,A,B));
 70     }
 71 }st1,st2;
 72 
 73 void data_in()
 74 {
 75     scanf("%d",&N);
 76     for(int i=1;i<=N;i++) scanf("%d",&A[i]);
 77     scanf("%d",&M);
 78     for(int i=1;i<=M;i++){
 79         scanf("%d%d",&q[i].l,&q[i].r);
 80         q[i].id=i;
 81     }
 82 }
 83 int id(int v){ return upper_bound(B+1,B+tot+1,v)-B-1; }
 84 void solve()
 85 {
 86     st1.init(); st2.init();
 87     st1.build(st1.rt,1,N); st2.build(st2.rt,1,tot);
 88     int p=1;
 89     st1.update2(st1.rt,1,N,1,A[1]);
 90     st2.update2(st2.rt,1,tot,Rank[1],1);
 91     for(int i=2;i<=N;i++){
 92         int j=st1.query_p(st1.rt,1,N,A[i]);
 93         if(j!=-1){
 94             st1.update1(st1.rt,1,N,j,A[j]-A[i]);
 95             while((j=st2.query_max(st2.rt,1,tot,Rank[i],id((A[i]+A[j])/2)))!=-1){
 96                 st1.update1(st1.rt,1,N,j,A[j]-A[i]);
 97                 if(A[j]-A[i]==0) break;
 98             }
 99         }
100         while(p<=M&&q[p].r<=i){
101             ans[q[p].id]=min(ans[q[p].id],st1.query_min(st1.rt,1,N,q[p].l,q[p].r));
102             p++;
103         }
104         if(p>M) break;
105         st1.update2(st1.rt,1,N,i,A[i]);
106         st2.update2(st2.rt,1,tot,Rank[i],i);
107     }
108 }
109 void work()
110 {
111     memcpy(B,A,sizeof(A));
112     sort(B+1,B+N+1);
113     tot=unique(B+1,B+N+1)-B-1;
114     
115     for(int i=1;i<=N;i++) Rank[i]=lower_bound(B+1,B+tot+1,A[i])-B;
116     for(int i=1;i<=M;i++) ans[i]=inf;
117     sort(q+1,q+M+1);
118     solve();
119     
120     int l=1,r=N;
121     while(l<r) swap(Rank[l],Rank[r]),swap(A[l++],A[r--]);
122     for(int i=1;i<=M;i++){
123         swap(q[i].l,q[i].r);
124         q[i].l=N-q[i].l+1,q[i].r=N-q[i].r+1;
125     }
126     sort(q+1,q+M+1);
127     solve();
128     
129     for(int i=1;i<=M;i++) printf("%d\n",ans);
130 }
131 int main()
132 {
133     data_in();
134     work();
135     return 0;
136 }
View Code

 

posted @ 2018-04-09 21:48  KKKorange  阅读(341)  评论(0编辑  收藏  举报