HDU 5289 Assignment(单调队列)

题意:给T足数据,然后每组一个n和k,表示n个数,k表示最大允许的能力差,接下来n个数表示n个人的能力,求能力差在k之内的区间有几个


分析:维护一个区间的最大值和最小值,使得他们的差小于k,于是采用单调队列


普通单调队列做法:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e6+5;
int a[maxn];
struct node{
    int index;
    int v;
}qd[maxn];
node qx[maxn];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int st1,st2,ed1,ed2;
        st1=st2=ed1=ed2=1;
        long long sum=0;
        int j=1;
        for(int i=1;i<=n&&j<=n;i++){        
            if(i==1){
                qd[1].index=qx[1].index=1;
                qd[1].v=qx[1].v=a[1];
            }
            else{
                while(st1<=ed1){                //单调队列维护最大值
                    if(qd[ed1].v<=a[i]) ed1--;  //比a[i]小的而且下标比i小的出队列
                    else break;
                }
                qd[++ed1].v=a[i];               //a[i]入队列
                qd[ed1].index=i;
                while(st2<=ed2){                //单调队列维护最小值
                    if(qx[ed2].v>=a[i]) ed2--;  //比a[i]大而且下标比i小的出队列
                    else break;
                }
                qx[++ed2].v=a[i];               //a[i]入队列
                qx[ed2].index=i;
                while(qd[st1].v-qx[st2].v>=k&&st1<=ed1&&st2<=ed2)   //计数
                {
                    if(qd[st1].index==j) st1++;
                    if(qx[st2].index==j) st2++;
                    sum+=(i-j);
                    j++;
                }
            }
        }
        while(j<=n) {
            sum+=(n-j+1);
            j++;
        }
        printf("%I64d\n",sum);
    }
}

二分单调队列做法:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e6+5;
int a[maxn];
struct node{
    int index;
    int v;
}qd[maxn];
node qx[maxn];
int maxc(int l,int r,int d){        //二分找出d入队列的为止
    while(l<=r){
        int mid=(l+r)/2;
        if(qd[mid].v==d) return mid;
        else if(qd[mid].v>d) l=mid+1;
        else r=mid-1;
    }
    return l;
}
int minc(int l,int r,int d){        //二分找出d入队列的为止
    while(l<=r){
        int mid=(l+r)/2;
        if(qx[mid].v==d) return mid;
        else if(qx[mid].v<d) l=mid+1;
        else r=mid-1;
    }
    return l;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int st1,st2,ed1,ed2;
        st1=st2=ed1=ed2=1;
        long long sum=0;
        int j=1;
        for(int i=1;i<=n&&j<=n;i++){
            if(i==1){
                qd[1].index=1;
                qd[1].v=a[1];
                qx[1].index=1;
                qx[1].v=a[1];
            }
            else{
                ed1=maxc(st1,ed1,a[i]); //二分找出d入队列的为止,维护最大值
                qd[ed1].v=a[i];         
                qd[ed1].index=i;
                ed2=minc(st2,ed2,a[i]); //二分找出d入队列的为止,维护最小值
                qx[ed2].v=a[i];
                qx[ed2].index=i;
                while(qd[st1].v-qx[st2].v>=k&&st1<=ed1&&st2<=ed2)//计数
                {
                    if(qd[st1].index==j) st1++;
                    if(qx[st2].index==j) st2++;
                    sum+=(i-j);
                    j++;
                }
            }
        }
        while(j<=n) {
            sum+=(n-j+1);
            j++;
        }
        printf("%I64d\n",sum);
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值