大意:
求最长波动序列
设f[i]为dp到第i位、以波峰结尾的最长波动序列,
设g[i]为dp到第j位、以波谷结尾的最长波动序列。
则f[i]可以由满足h[j] < h[i]的g[j]转来,
g[i]可以由满足h[j] > h[i]的f[j]转来。
则当转移f[i]的时候,需要找到之前出现过的、高度小于h[i]的且g[j]最大的点
用h[i]的值域建树,maxf/maxg[h[i]]表示以高度h[i]结尾的最大f[i]/g[i]
每获得一个新的f[i]或g[i],更新maxf/maxg[h[i]]。
那转移f[i]的时候,询问(0,h[i] - 1)区间中最大的maxg就可以了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#define L(w) w << 1
#define R(w) w << 1|1
#define INF 1061109567
using namespace std;
const int MAXN = 200000 + 50;
int L[MAXN << 2],R[MAXN << 2],maxf[MAXN << 2],maxg[MAXN << 2];
int n,h[MAXN],maxn;
void update(int w)
{
maxf[w] = max(maxf[L(w)],maxf[R(w)]);
maxg[w] = max(maxg[L(w)],maxg[R(w)]);
}
void build_tree(int w,int l,int r)
{
L[w] = l;R[w] = r;
if(l == r)
{
maxf[w] = -INF;
maxg[w] = -INF;
return;
}
int mid = L[w] + R[w] >> 1;
build_tree(L(w),l,mid);
build_tree(R(w),mid + 1,r);
update(w);
}
int f[MAXN],g[MAXN];
void add(int w,int loc,int x,int ch)
{
if(L[w] == R[w])
{
if(ch)
{
maxf[w] = max(maxf[w],x);
}
else
{
maxg[w] = max(maxg[w],x);
}
return;
}
int mid = L[w] + R[w] >> 1;
if(loc <= mid)add(L(w),loc,x,ch);
else add(R(w),loc,x,ch);
update(w);
}
int ask(int w,int l,int r,int v)
{
if(L[w] == l && R[w] == r)
{
return v ? maxg[w] : maxf[w];
}
int mid = L[w] + R[w] >> 1;
if(r <= mid)return ask(L(w),l,r,v);
else if(l > mid)return ask(R(w),l,r,v);
else return max(ask(L(w),l,mid,v),ask(R(w),mid + 1,r,v));
}
int ans;
int tmp[MAXN];
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
scanf("%d",&h[i]),tmp[i] = h[i];
sort(tmp + 1,tmp + n + 1);
int tot = unique(tmp + 1,tmp + n + 1) - tmp - 1;
for(int i = 1;i <= n;i ++)
h[i] = lower_bound(tmp + 1,tmp + tot + 1,h[i]) - tmp,maxn = max(maxn,h[i]);
build_tree(1,0,maxn + 1);
f[1] = g[1] = 1;
add(1,h[1],f[1],1);
add(1,h[1],g[1],0);
for(int i = 2;i <= n;i ++)
{
f[i] = ask(1,0,h[i] - 1,1) + 1;
g[i] = ask(1,min(maxn,h[i] + 1),maxn,0) + 1;
add(1,h[i],f[i],1);
add(1,h[i],g[i],0);
ans = max(ans,max(f[i],g[i]));
}
printf("%d",ans);
return 0;
}
转化类似于权值线段树?