题意:
给定一个数组序列,找到一段区间 [ i , j ],使得区间a[i] < a[k] < a[j],( k 是 [ i , j ] 中任意一个数),此处注意 a[i] 是区间唯一最小值,a[j] 是区间唯一最大值。
求 j-i 最大值。(n <= 50000)
思路:
确定左端点,然后找到一个最长区间,使得该区间的最小值为左端点这个值,然后再求出这个区间的最大值,更新答案即可。
所以现在的问题就变成了如何求出最长区间,使得区间最小值为左端点这个值。
最简单的思路就是枚举了,但是时间复杂度上肯定不可行,因此可以考虑进行二分查询,二分右端点,判断这个区间的最小值是否为左端点,即可求出最长的可行区间。
因为不涉及修改,所以如果用 st 表的话,时间复杂度为O(nlogn),如果用线段树的话,时间复杂度为O(n(logn^2)),可能会过不去,需要在二分的时候进行一些优化操作。
总结:
这是一个基础的 线段树+二分 问题,一直T了好久,还是需要加强码力才行。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define rep(i,a,b) for(int i = a; i <= b; i++)
using namespace std;
const int N = 50000+1000;
const int inf = 0x3fffffff;
int n,a[N],lar[N];
struct Tree{
int l,r;
int minn,maxn;
int pos;
}t[4*N];
inline int gi()
{
int date = 0,m = 1; char ch = 0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch = getchar();
if(ch=='-'){m = -1; ch = getchar();}
while(ch>='0' && ch<='9')
{
date = date*10+ch-'0';
ch = getchar();
}return date*m;
}
void build(int p,int l,int r)
{
t[p].l = l, t[p].r = r;
if(l == r){
t[p].maxn = t[p].minn = a[l];
t[p].pos = l;
return;
}
int mid = (l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
if(t[p*2].maxn > t[p*2+1].maxn)
t[p].maxn = t[p*2].maxn, t[p].pos = t[p*2].pos;
else
t[p].maxn = t[p*2+1].maxn, t[p].pos = t[p*2+1].pos;
t[p].minn = min(t[p*2].minn,t[p*2+1].minn);
}
int ask_min(int p,int l,int r)
{
if(l <= t[p].l && r >= t[p].r) return t[p].minn;
int mid = (t[p].l+t[p].r)>>1;
int val = inf;
if(l <= mid) val = min(val,ask_min(p*2,l,r));
if(r > mid) val = min(val,ask_min(p*2+1,l,r));
return val;
}
pair<int,int> ask_max(int p,int l,int r)
{
if(l <= t[p].l && r >= t[p].r) return make_pair(t[p].maxn,t[p].pos);
int mid = (t[p].l+t[p].r)>>1;
pair<int,int> tmp(-1,0),tp1;
if(l <= mid){
tp1 = ask_max(p*2,l,r);
if(tmp < tp1) tmp = tp1;
else if(tmp == tp1) tmp.second = min(tmp.second,tp1.second);
}
if(r > mid){
tp1 = ask_max(p*2+1,l,r);
if(tmp < tp1) tmp = tp1;
else if(tmp == tp1) tmp.second = min(tmp.second,tp1.second);
}
return tmp;
}
int main()
{
// while(~scanf("%d",&n))
while(scanf("%d", &n) != EOF)
{
rep(i,1,n) a[i] = gi();
int ans = -1;
build(1,1,n);
rep(i,1,n){
int l = i+1,r = n,cr = -1;
while(l <= r){
int mid = (l+r)>>1;
if((mid-i) <= ans){
l = mid+1;
continue;
}
int tmp = ask_min(1,i,mid);
// printf("i:%d,mid:%d,tmp:%d\n",i,mid,tmp);
if(tmp < a[i]) r = mid-1;
else cr = mid, l = mid+1;
}
// printf("cr:%d\n",cr);
if(cr != -1 && (cr-i) > ans){
pair<int,int> tmp = ask_max(1,i,cr);
if(tmp.first > a[i])
ans = max(ans,tmp.second-i);
}
}
printf("%d\n",ans);
}
return 0;
}