2018宁夏icpc邀请赛 L题(线段树+单调栈)

题意:给你一个长度为n的序列,问你有多少个区间[ l , r ]满足条件:对该区间排序后,区间内相邻的两个数差小于等于1.

题目链接:https://cn.vjudge.net/problem/Gym-102222L

这道题是一道线段树的好题,不得不感叹一下线段树的强大。

首先我们转换一下题意,区间[ l , r ] 满足上面条件,可以转换为该区间满足以下条件:mx-mn+1==cnt,其中mx代表区间最大值,mn代表区间最小值,cnt代表区间有多少个重复的数。

然后对于问有多少个区间满足某某条件,通常做法就是枚举区间右端点,然后用什么数据结构来找有多少个左端点可以与该右端点构成满足题意的区间。

由于mx-mn-cnt >= -1,所以我们是枚举右端点,线段树的点代表该点作为左端点与现在枚举的右端点构成的区间的mx-mn-cnt。线段树维护区间的最小值(一定是-1)以及有多少个最小值

然后单调栈维护mx和mn,每次右移右端点就更新。

然后对于cnt,主席树有个套路就是维护每个数最后一次出现的位置,这里也是,维护每个数最后出现的位置。

然后网上有一些代码写了个query函数。。。因为枚举右端点r,肯定有一个区间[ r,r ]的mx-mn-cnt值为-1,所以线段树最小值绝对是-1,所以只需要找tr[1].num就好了。

  1 /*************************************************************************
  2     > File Name: yinchuanL.cpp
  3 # File Name: yinchuanL.cpp
  4 # Author : xiaobuxie
  5 # QQ : 760427180
  6 # Email:[email protected]
  7 # Created Time: 2019年09月04日 星期三 14时49分48秒
  8  ************************************************************************/
  9 
 10 #include
 11 #include
 12 #include
 13 #include
 14 #include
 15 #include<set>
 16 #include
 17 #include
 18 #include
 19 using namespace std;
 20 typedef long long ll;
 21 #define inf 0x3f3f3f3f
 22 #define pq priority_queue,greater >
 23 ll gcd(ll a,ll b){
 24     if(areturn gcd(b,a);
 25     return b==0?a:gcd(b,a%b);
 26 }
 27 
 28 
 29 const int N=1e5+9;
 30 ll a[N];
 31 int topmx,topmn;
 32 map<int,int> mp;
 33 struct node{
 34     ll val;
 35     int pos;
 36 }stamx[N],stamn[N];
 37 struct tree{
 38     ll mn,num,lazy;
 39 }tr[N<<2];
 40 void push_up(int o){
 41     tr[o].mn=min(tr[o<<1].mn,tr[o<<1|1].mn);
 42     tr[o].num=0;
 43     if(tr[o].mn==tr[o<<1].mn) tr[o].num+=tr[o<<1].num;
 44     if(tr[o].mn==tr[o<<1|1].mn) tr[o].num+=tr[o<<1|1].num;
 45 }
 46 void push_down(int o){
 47     if(tr[o].lazy){
 48         tr[o<<1].lazy+=tr[o].lazy;
 49         tr[o<<1|1].lazy+=tr[o].lazy;
 50         tr[o<<1].mn+=tr[o].lazy;
 51         tr[o<<1|1].mn+=tr[o].lazy;
 52         tr[o].lazy=0;
 53     }
 54 }
 55 void build(int o,int l,int r){
 56     tr[o]=(tree){0,r-l+1,0};
 57     if(l==r){
 58         return;
 59     }
 60     int m=(l+r)>>1;
 61     build(o<<1,l,m);
 62     build(o<<1|1,m+1,r);
 63 }
 64 void change(int o,int l,int r,int x,int y,ll v){
 65     if(x<=l && r<=y){
 66         tr[o].lazy+=v;
 67         tr[o].mn+=v;
 68         return;
 69     }
 70     int m=(l+r)>>1;
 71     push_down(o);
 72     if(x<=m) change(o<<1,l,m,x,y,v);
 73     if(y>m)  change(o<<1|1,m+1,r,x,y,v);
 74     push_up(o);
 75 }
 76 int main(){
 77     int T; scanf("%d",&T);
 78     for(int cas=1;cas<=T;++cas){
 79         int n; scanf("%d",&n);
 80         mp.clear();
 81         for(int i=1;i<=n;++i) scanf("%lld",a+i);
 82         build(1,1,n);
 83         ll ans=0;
 84         topmx=topmn=0;
 85         for(int i=1;i<=n;++i){
 86             //cerr<
 87             while(topmx && stamx[topmx].val < a[i]){
 88                 int p=stamx[topmx].pos;
 89                 ll tem=a[i]-stamx[topmx].val;
 90                 change(1,1,n,stamx[--topmx].pos+1,p,tem);
 91             }
 92             stamx[++topmx]=(node){a[i],i};
 93 
 94             while(topmn && stamn[topmn].val > a[i]){
 95                 int p=stamn[topmn].pos;
 96                 ll tem=stamn[topmn].val-a[i];
 97                 change(1,1,n,stamn[--topmn].pos+1,p,tem);
 98             }
 99             stamn[++topmn]=(node){a[i],i};
100             //cerr<
101             change(1,1,n,mp[a[i]]+1,i,-1);
102             mp[a[i]]=i;
103             ans+=tr[1].num;
104         }
105         printf("Case #%d: %lld\n",cas,ans);
106     }
107     return 0;
108 }
View Code

 

转载于:https://www.cnblogs.com/xiaobuxie/p/11465900.html

你可能感兴趣的:(2018宁夏icpc邀请赛 L题(线段树+单调栈))