2019年ACM-ICPC - 银川网络赛L:Continuous Intervals【线段树+单调栈】

题目:

2019银川网络赛L:Continuous Intervals

题意:

定义一个连续区间:如果将 Array[L......R] 排序后,相邻元素的差的绝对值不大于 0,则称其为一个连续区间,现在给定包含 N个元素的数组,求有多少个连续区间

分析:

虽然网络选拔赛直接拿原题有点恶心,但题还是好题

如果一个区间 [L......R] 符合连续区间的定义,设这个区间内的最大值为 max,最小值为 min,不同元素的个数为 cnt,那么会有:max-min+1=cnt,这类统计区间个数的问题,需要套路的枚举一个端点,然后统计有多少个另一个端点符合条件,枚举左端点相当于删除数,枚举右端点相当于添加数;转化一下公式:max-min-cnt=-1,而对于任意区间有如下推论:max-min-cnt>=-1,这意味着我们只需要维护区间中 max-min-cnt 的最小值及其个数,就可以快速得到另一个端点的数量;添加数容易维护区间最值,所以考虑枚举右端点 R,每次添加一个数(移动右端点),我们需要知道这个数作为最大值能影响的最远左端点 L,这个可以通过单调栈得到,那么我们就需要修改 [L,R] 的最大值;如果直接暴力修改,复杂度太高,考虑在 max-min-cnt 的值上再加上即将修改的最大值和原有最大值的差值就行了,所以还得记录每个最大值影响的区间,最小值同理,只需要用 map 记录每个数上次出现的位置就可维护 cnt

代码:

#include <bits/stdc++.h>

#define fi first
#define se second
#define pii pair<int,int>
#define PII pair<int,pii>
#define sz(x) (int)(x).size()
using namespace std;
typedef long long LL;
const int maxn = 1e5+16;
int T,n,m,a[maxn];
map<int,int> mp;
stack<PII> s1,s2;          //PII.fi代表数值,PII.se代表这个数值影响的区间
LL lazy[maxn<<2],MIN[maxn<<2],cnt[maxn<<2];
inline void pushdown(int x){
    MIN[x<<1] += lazy[x]; MIN[x<<1|1] += lazy[x];
    lazy[x<<1] += lazy[x]; lazy[x<<1|1] += lazy[x];
    lazy[x] = 0; 
}
void BuildTree(int l,int r,int x){
    cnt[x] = MIN[x] = lazy[x] = 0;
    if(l == r){
        cnt[x] = 1;
        return ;
    }
    int mid = (l+r) >> 1;
    BuildTree(l,mid,x<<1);
    BuildTree(mid+1,r,x<<1|1);
}
void UpdataTree1(int l,int r,int L,int R,int x,LL val){
    if(l > R || r < L) return;
    if(l >= L && r <= R){
        MIN[x] += val;
        lazy[x] += val;
        return ;
    }
    if(lazy[x]) pushdown(x);
    int mid = (l+r)>>1; 
    UpdataTree1(l,mid,L,R,x<<1,val);
    UpdataTree1(mid+1,r,L,R,x<<1|1,val);
    MIN[x] = min(MIN[x<<1],MIN[x<<1|1]); cnt[x] = 0;
    if(MIN[x] == MIN[x<<1]) cnt[x] += cnt[x<<1];
    if(MIN[x] == MIN[x<<1|1]) cnt[x] += cnt[x<<1|1]; 
}
void QueryTree(int l,int r,int L,int R,int x,LL &res){
    if(l > R || r < L) return ;
    if(l >= L && r <= R){
        if(MIN[x] == -1) res += cnt[x];
        return ;
    }
    if(lazy[x]) pushdown(x);
    int mid = (l+r) >> 1;
    QueryTree(l,mid,L,R,x<<1,res);
    QueryTree(mid+1,r,L,R,x<<1|1,res);
}
LL solve(){
    mp.clear(); LL res = 0;
    while(!s1.empty()) s1.pop();
    while(!s2.empty()) s2.pop();
    s1.push(PII(2e9,pii(0,0))); s2.push(PII(0,pii(0,0)));
    for(int i = 1;i <= n; ++i){
        UpdataTree1(1,n,i,i,1,-1);
        if(!mp.count(a[i])) UpdataTree1(1,n,1,i-1,1,-1);
        else UpdataTree1(1,n,mp[a[i]]+1,i-1,1,-1);
        int last = i;
        while(!s1.empty()&&s1.top().fi<a[i]){
            UpdataTree1(1,n,s1.top().se.fi,last-1,1,a[i]-s1.top().fi);
            last = s1.top().se.fi; s1.pop();
        }
        last = i;
        while(!s2.empty()&&s2.top().fi>a[i]){
            UpdataTree1(1,n,s2.top().se.fi,last-1,1,s2.top().fi-a[i]);
            last = s2.top().se.fi; s2.pop();
        }
        mp[a[i]] = i; s1.push(PII(a[i],pii(s1.top().se.se+1,i)));s2.push(PII(a[i],pii(s2.top().se.se+1,i)));
        QueryTree(1,n,1,i,1,res);
    }
    return res;
}
int main(){
    int T; cin >> T;
    for(int Case = 1;Case <= T; ++Case){
        scanf("%d",&n); BuildTree(1,n,1);
        for(int i = 1;i <= n; ++i) scanf("%d",a+i);
        printf("Case #%d: %lld\n",Case,solve());
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值