题解 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 表,比较懒就不打了。
方法五
承接我们方法二的双指针,我们的难点其实只是在与删除,在加点时我们并没有难度,我们可以用单调队列来维护我们的删除操作。

浙公网安备 33010602011771号