hdu-5289

3 篇文章 0 订阅
3 篇文章 0 订阅

题目:点击打开链接
题意:给出一个数列,问其中存在多少连续子序列,子序列的最大值-最小值< k 。
分析:

解法一:二分+RMQ,枚举左端点,RMQ预处理(st表),二分找到每个左端点满足条件的右界。

解法二:单调队列,维护两个单调队列,一个单调递增,一个单调递减,双指针,第一个第二个指针初始指向第一个数据,第一个指针按顺序不断向队尾添加数据,当最大值最小值的差大于等于k后,就意味着新添加的这个不能作用于当前第二个指针的位置,也就能计算出,以第二个指针位置开始的连续子序列的个数,最后统计总和。双端队列可以用数组模拟,我这里用的stl的deque,熟悉一下基本用法。

补充:二分+线段树也可以,枚举左端点,线段树维护区间最值,二分找到区间最大值-最小值<k的右边界。
代码一(二分+RMQ): 

#pragma comment(linker, "/STACK:102400000,102400000")
#include<unordered_map>
#include<unordered_set>
#include<algorithm>
#include<iostream>
#include<fstream>
#include<complex>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<iomanip>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cctype>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#include<map>
using namespace std;
#define pt(a) cout<<a<<endl
#define debug test
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pii pair<int,int>
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define inf 0x3f3f3f3f
#define eps 1e-10
#define PI acos(-1.0)
const ll mod = 1e9+7;
const int N = 1e5+10;

ll qp(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
int to[4][2]={{-1,0},{1,0},{0,-1},{0,1}};

int t,n,k,mi[N][20],mx[N][20];

void rmq() {
    for(int j=1;j<20;j++)
        for(int i=1;i<=n;i++)
        if(i+(1<<j)-1<=n) {
            mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
            mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
        }
}

int qy(int l,int r) {
    int kk=log2(r-l+1);
    int s1=max(mx[l][kk],mx[r-(1<<kk)+1][kk]);
    int s2=min(mi[l][kk],mi[r-(1<<kk)+1][kk]);
    return s1-s2;
}

int main() {
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);

    while(~scanf("%d",&t)) {
        while(t--) {
            scanf("%d%d",&n,&k);
            for(int i=1;i<=n;i++) {
                scanf("%d",&mi[i][0]);
                mx[i][0]=mi[i][0];
            }
            rmq();
            ll ans=0,l=1,r=n;
            for(int i=1;i<=n;i++) {
                l=i,r=n;
                while(l<=r) {
                    int m=(l+r)/2;
                    if(qy(i,m)<k) l=m+1;
                    else r=m-1;
                }
                ans+=r-i+1;
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}

 

代码二(单调队列):

#pragma comment(linker, "/STACK:102400000,102400000")
///#include<unordered_map>
///#include<algorithm>
#include<iostream>
#include<fstream>
#include<complex>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<iomanip>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cctype>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#include<map>
using namespace std;
#define pt(a) cout<<a<<endl
#define debug test
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pii pair<int,int>
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define inf 0x3f3f3f3f
#define eps 1e-10
#define PI acos(-1.0)
const ll mod = 1e9+7;
const int N = 1e5+10;

ll qp(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
int to[4][2]={{-1,0},{1,0},{0,-1},{0,1}};

int t,n,k,a[N];
deque<int> mx,mi;

int main() {
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);

    while(~scanf("%d",&t)) {
        while(t--) {
            scanf("%d%d",&n,&k);
            for(int i=1;i<=n;i++) scanf("%d",&a[i]);
            mx.clear(),mi.clear();
            ll ans=0;
            int i=1,j=1;
            for(;i<=n;i++) {
                while( !mx.empty() && mx.back()<a[i] ) mx.pop_back();
                mx.push_back(a[i]);
                while( !mi.empty() && mi.back()>a[i] ) mi.pop_back();
                mi.push_back(a[i]);
                while( !mx.empty() && !mi.empty() && mx.front()-mi.front()>=k ) {///不满足条件时加上以j指针开始的贡献
                    ans+=(i-j);
                    if(mx.front()==a[j]) mx.pop_front();
                    if(mi.front()==a[j]) mi.pop_front();
                    j++;
                }
            }
            ///注意剩下的
            while(j<=n) {
                ans+=(i-j);
                j++;
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值