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

题解 HDU - 3530 Subsequence

题目描述

给出一个长度为 n 的数列,求一个最长的区间,使得区间中最小值和最大值的差在mk
1n105,序列中的元素范围 [0,106]

dt=max(l,r)min(l,r)

方法一 O(nlogn+nlogn)

因为考试的时候没有马上想到正解,所以随便就打上了一个 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(nlogn+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(nlogn+nlogn)

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

方法五

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

posted @   djh0314  阅读(10)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示