把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

题解 HDU - 3530 Subsequence

题目描述

给出一个长度为 $n$ 的数列,求一个最长的区间,使得区间中最小值和最大值的差在$m\sim k$。
$1\le n \le 10^5$,序列中的元素范围 $[0,10^6]$。

令 $dt=\max(l,r)-\min(l,r)$。

方法一 $O(n\log n+n\log n)$

因为考试的时候没有马上想到正解,所以随便就打上了一个 ST 表,
于是我们可以枚举左端点,然后,我们在 $l$ 固定时,很容易想到,紧随着 $r$ 的提高,我们的 $dt$ 也会随之提高。
于是我们就可以马上想到我们可以用二分来计算每个 $l$ 的最大 $r$。

Code。

#include <bits/stdc++.h>
//#define int long long
using namespace std;
const int N = 1e5+5;
int n, m,K;
int a[N];
int st[2][N][20];
int d[N];
inline int query(int l,int r,int opt) {
    int len=r-l+1;
    if(opt) return max(st[1][l][d[len]],st[1][r-(1<<d[len])+1][d[len]]);
    return min(st[0][l][d[len]],st[0][r-(1<<d[len])+1][d[len]]);
}
inline int check(int l,int r) {
    return query(l,r,1)-query(l,r,0);
}
signed main() {
    for(int i=2; i<N; ++i) d[i]=d[i/2]+1;
    while(cin>>n>>m>>K) {
        for(int i=1; i<=n; ++i) cin>>a[i],st[0][i][0]=st[1][i][0]=a[i];
        for(int i=1; i<20; ++i) {
            for(int j=1; j+(1<<i)-1<=n; ++j) {
                st[1][j][i]=max(st[1][j][i-1],st[1][j+(1<<i-1)][i-1]);
                st[0][j][i]=min(st[0][j][i-1],st[0][j+(1<<i-1)][i-1]);
            }
        }
        int ans=0;
        for(int i=1; i<=n; ++i) {
            int l=i,r=n,res=-1;
            while(l<=r) {
                int mid=l+r>>1;
                int dt=check(i,mid);
                if(dt<=K) {
                    if(m<=dt)
                        res=mid;
                    l=mid+1;
                } else r=mid-1;
            }
            if(res!=-1) ans=max(ans,res-i+1);
        }
        cout<<ans<<endl;
    }
    return 0;
}

方法二 $O(n\log n +n)$

与方法一相同的,依旧是基于 ST 表,但是,当相同的 $r$,在 $l$ 上升的过程中,$dt$ 也是会变小,那我们就可以用双指针代替我们的枚举+二分。

Code

#include <bits/stdc++.h>
//#define int long long
using namespace std;
const int N = 1e5+5;
int n, m,K;
int a[N];
int st[2][N][20];
int d[N];
inline int query(int l,int r,int opt) {
    int len=r-l+1;
    if(opt) return max(st[1][l][d[len]],st[1][r-(1<<d[len])+1][d[len]]);
    return min(st[0][l][d[len]],st[0][r-(1<<d[len])+1][d[len]]);
}
inline int check(int l,int r) {
    return query(l,r,1)-query(l,r,0);
}
signed main() {
    for(int i=2; i<N; ++i) d[i]=d[i/2]+1;
    while(cin>>n>>m>>K) {
        for(int i=1; i<=n; ++i) cin>>a[i],st[0][i][0]=st[1][i][0]=a[i];
        for(int i=1; i<20; ++i) {
            for(int j=1; j+(1<<i)-1<=n; ++j) {
                st[1][j][i]=max(st[1][j][i-1],st[1][j+(1<<i-1)][i-1]);
                st[0][j][i]=min(st[0][j][i-1],st[0][j+(1<<i-1)][i-1]);
            }
        }
        int ans=0;
        int r=1;
        for(int i=1; i<=n; ++i) {
            while(r<=n) {
                int dt=check(i,r);
                if(dt<=K) ++r;
                else break;
            }
            if(m<=check(i,r-1))
                ans=max(ans,r-i);
        }
        cout<<ans<<endl;
    }
    return 0;
}

方法三、四 $O(n\log n +n\log n )$

其实也不能算新方法,就是用线段树来替换我们的 ST 表,比较懒就不打了。

方法五

承接我们方法二的双指针,我们的难点其实只是在与删除,在加点时我们并没有难度,我们可以用单调队列来维护我们的删除操作。

posted @ 2023-05-06 21:13  djh0314  阅读(25)  评论(0)    收藏  举报  来源
浏览器标题切换
浏览器标题切换end