dlutoj 单调队列

题目链接:http://acm.dlut.edu.cn/problem.php?id=1327

题目:(不知道外面能不能上 = =)

Description

Shik loves sorted intervals. But currently he does not have enough time to sort all the numbers. So he decided to use Almost sorted intervals. An Almost sorted interval is a consecutive subsequence in a sequence which satisfies the following property:
The first number is the smallest.
The last number is the largest.
Please help him count the number of almost sorted intervals in this permutation.
Note: Two intervals are different if at least one of the starting or ending indices are different.

Input

Muti-case , for each case
The first line contains an integer N. 
The second line contains a permutation from 1 to N
1<=n<=10^6

Output

Output the number of almost sorted intervals.

Sample Input

53 1 2 5 4

Sample Output

8

HINT

Explanation 

The subsequences [3], [1], [1 2], [1 2 5], [2], [2 5], [5], [4] are almost sorted intervals.

思路:题意很简单,就是求某种区间的个数,这个区间满足区间左端点是这个区间的最小值,区间的右端点是这个区间的最大值,问满足这个条件的区间的个数

首先,我们能用单调队列求出来点i作为区间左端点的时候的最大的可能的右端点范围,为[i,r[i]]。然后求完之后,从右向左进行单调队列的过程,维护最大值,当把点i放进单调队列的时候,单调队列中的点都大于等于点i的值,也就是可能作为右端点,这时候还需要满足点i作为左端点的时候能够覆盖右端点,于是二分寻找,就得到了每个点作为左端点的值,然后得到最终结果。

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <map>
using namespace std;
#define ll long long
const int maxn = 1e6+10;
struct Node{
    int val,tt;
}node[maxn],mmin[maxn],mmax[maxn];
struct NN{
    int tt,r;
}rr[maxn];
int r[maxn];
void get_min(int n){
    int head = 0,tail = -1;
    for(int i = 0;i < n;i ++){
        while(tail >= head&&mmin[tail].val >= node[i].val){
            r[mmin[tail].tt] = i-1;
            tail --;
        }
        mmin[++ tail] = node[i];
    }
}
ll ans;
int get(int l,int r,int vv){
    int m;
    while(l < r){
        m = (l+r)>>1;
        if(mmax[m].tt > vv)l = m+1;
        else r = m;
    }
    return l;
}
void get_max(int n){
    int head = 0,tail = -1;
    for(int i = n-1;i >= 0;i --){
        while(tail >= head&&mmax[tail].val <= node[i].val){
            tail --;
        }
        mmax[++ tail] = node[i];
        //for(int i = head;i <= tail;i ++)cout<<mmax[i].val<<'_';cout<<endl;
        //cout<<i<<' '<<tail-get(head,tail,(r[i] == -1?n:r[i]))+1<<' '<<(r[i]==-1?n:r[i])<<endl;
        ans = ans + tail - get(head,tail,r[i]==-1?n:r[i]) + 1;
    }
}
int nn[maxn];
bool cmp(NN a,NN b){
    return a.r < b.r;
}
int main(){
    int n,k;
    while(~scanf("%d",&n)){
        memset(r,-1,sizeof(r));
        memset(nn,0,sizeof(nn));
        for(int i = 0;i < n;i ++){
            scanf("%d",&node[i].val);
            node[i].tt = i;
        }
        get_min(n);
        ans = 0;
        get_max(n);
        cout<<ans<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值