1006 How many?

Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 36 Accepted: 2

Description

有一天,小Q给了小J一些数字,让小J帮忙找到其中最大的数,由于小J是一个程序猿,当然写了一个代码很快的解决了这个问题。这时,邪恶的小Y又出现了,他问小J,假如我只需要知道这些数字中的某个区间的最大值,你还能做嘛?小J经过七七四十九天的思考,终于完美的解决了这道题目,这次,他想也让小Y尝尝苦头,于是他问小Y,我现在想知道存在多少不同的区间的最大值大于等于k,你还能做吗?这次,小Y犯了难,他来请教身为程序猿的你。

Hint:一个区间指alal+1, …, ar,这一段的数且<= r,一个区间的最大值指max{ alal+1, …, a},两个区间[l1r1]、 [l2r2]不同当且仅当l1不等于l2r1不等于r2

Input

第一行为一个整数T(1<=T<=5),代表样例数。接下来每组样例的第一行读入一个正整数n(1<=n<=100000)。接下来一行读入n个正整数a(1 <= ai <= 100000)。接下来一行一个正整数Q(1<=Q<=100000),表示有Q组询问。接下来Q行,每行一个正整数k(1<= <=100000)。

Output

对于每组样例,先输出“Case #ca:”,ca从1开始。接下来Q行,每行一个正整数,表示存在多少区间大于等于k

Sample Input

 
 
1 3 1 2 3 3 1 2 3

Sample Output

 
 
Case #1: 6 5

3

这题可以用单调队列做,先处理出以每个数为最大值的区间,然后再把询问都读入,然后按询问的值从大到小排序,然后每次把数字大于等于询问值的区间都加进去,加进去的值为(idx-l[i]+1)*(r[i]-idx+1),注意,如果出现5 4 5的情况,即第一个5和第二个5的边界是相同的,那么就只能加一次。

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
#define inf 99999999
#define pi acos(-1.0)
#define maxn 100050
ll a[maxn];
struct node{
    int l,r,idx;
}temp;
vector<node>pos[maxn];
vector<node>::iterator it;
int q[1111111][2],l[maxn],r[maxn];

struct node1{
    int num,idx;
}question[maxn];
ll ans[maxn];

bool cmp(node1 a,node1 b){
    return a.num>b.num;
}


int main()
{
    int n,m,i,j,T,front,rear,t,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        for(i=1;i<=100000;i++)pos[i].clear();

        front=1;rear=0;
        for(i=1;i<=n;i++){
            while(front<=rear && q[rear][0]<=a[i])rear--;
            if(front>rear)l[i]=1;
            else l[i]=q[rear][1]+1;

            rear++;
            q[rear][0]=a[i];
            q[rear][1]=i;

        }

        front=1;rear=0;
        for(i=n;i>=1;i--){
            while(front<=rear && q[rear][0]<=a[i])rear--;
            if(front>rear)r[i]=n;
            else r[i]=q[rear][1]-1;

            rear++;
            q[rear][0]=a[i];
            q[rear][1]=i;

        }

        for(i=1;i<=n;i++){
            temp.l=l[i];
            temp.r=r[i];
            temp.idx=i;
            pos[a[i] ].push_back(temp);
        }
        scanf("%d",&t);
        for(i=1;i<=t;i++){
            scanf("%d",&question[i].num);
            question[i].idx=i;
        }
        sort(question+1,question+1+t,cmp);

        ll num=100005;
        ll sum=0;
        for(i=1;i<=t;i++){
            while(num>=question[i].num){
                ll pre=0,ll,rr;
                for(it=pos[num].begin();it!=pos[num].end();it++){
                    temp=*it;
                    ll=temp.l;
                    if(temp.l<=pre){
                        ll=pre+1;
                    }
                    rr=temp.r;
                    sum+=(temp.idx-ll+1)*(rr-temp.idx+1);
                    pre=temp.idx;
                }
                num--;
            }
            ans[question[i].idx]=sum;
        }
        cas++;
        printf("Case #%d:\n",cas);
        for(i=1;i<=t;i++){
            printf("%lld\n",ans[i]);
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值