【线段树】The 2019 Asia Yinchuan First Round Online Programming?- L - Continuous Intervals

题目链接https://nanti.jisuanke.com/t/41296


题意

给出一个序列,统计区间值域是连续的个数


题解

对于一段区间,如果 m a x [ l , r ] − m i n [ l , r ] + 1 = c n t [ l , r ] max[l,r]-min[l,r]+1=cnt[l,r] max[l,r]min[l,r]+1=cnt[l,r],那么这段区间就是符合条件的。
枚举右端点 r r r,那么就是要统计多少个 l l l,使得 m a x [ l , r ] − m i n [ l , r ] − c n t [ l , r ] + 1 = 0 max[l,r]-min[l,r]-cnt[l,r]+1=0 max[l,r]min[l,r]cnt[l,r]+1=0
右端点向右枚举的时候,每个 l l l m a x max max m i n min min c n t cnt cnt都会相应的变化。 m a x max max m i n min min可以用单调栈来维护, c n t cnt cnt用上一个出现的位置来维护。具体自己脑补。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> piir;
const int N=1e5+7;
const int inf=1e9+7;
int a[N];
int t[N<<2],lz[N<<2],num[N<<2];
void bd(int rt,int l,int r){
    t[rt]=1;
    num[rt]=1;
    lz[rt]=0;
    if(l==r) return;
    int m=l+r>>1;
    bd(rt<<1,l,m);
    bd(rt<<1|1,m+1,r);
    num[rt]=num[rt<<1]+num[rt<<1|1];
}
void pd(int rt){
    if(lz[rt]){
        t[rt<<1]+=lz[rt];
        t[rt<<1|1]+=lz[rt];
        lz[rt<<1]+=lz[rt];
        lz[rt<<1|1]+=lz[rt];
        lz[rt]=0;
    }
}
void upd(int rt,int l,int r,int L,int R,int val){
    if(L<=l&&r<=R){
        t[rt]+=val;
        lz[rt]+=val;
        return;
    }
    pd(rt);
    int m=l+r>>1;
    if(L<=m) upd(rt<<1,l,m,L,R,val);
    if(m<R) upd(rt<<1|1,m+1,r,L,R,val);
    t[rt]=min(t[rt<<1],t[rt<<1|1]);
    if(t[rt<<1]==t[rt<<1|1]) num[rt]=num[rt<<1]+num[rt<<1|1];
    else if(t[rt<<1]<t[rt<<1|1]) num[rt]=num[rt<<1];
    else num[rt]=num[rt<<1|1];
}
piir que(int rt,int l,int r,int L,int R){
    if(L<=l&&r<=R) return piir(t[rt],num[rt]);
    pd(rt);
    int m=l+r>>1;
    piir le=piir(inf,0),ri=piir(inf,0);
    if(L<=m) le=que(rt<<1,l,m,L,R);
    if(m<R) ri=que(rt<<1|1,m+1,r,L,R);
    if(le.first==ri.first) return piir(le.first,le.second+ri.second);
    if(le.first<ri.first) return le;
    return ri;
}

int T,n,cs;
unordered_map<int,int>pos;
int ma[N],ta;
int mi[N],ti;

int main()
{
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            pos[a[i]]=0;
        }
        bd(1,1,n);
        ta=ti=0;
        ll ans=0;
        for(int i=1;i<=n;i++){
            while(ta!=0&&a[ma[ta]]<a[i]){
                upd(1,1,n,ma[ta-1]+1,ma[ta],-a[ma[ta]]+a[i]);
                ta--;
            }
            ma[++ta]=i;
            while(ti!=0&&a[mi[ti]]>a[i]){
                upd(1,1,n,mi[ti-1]+1,mi[ti],+a[mi[ti]]-a[i]);
                ti--;
            }
            mi[++ti]=i;
            upd(1,1,n,pos[a[i]]+1,i,-1);
            pos[a[i]]=i;
            piir tmp=que(1,1,n,1,i);
            if(tmp.first==0) ans+=tmp.second;
        }
        printf("Case #%d: %lld\n",++cs,ans);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值