传送门:
题目描述:花匠栋栋种了一排花,每株花都有自己的高度。花儿越长越大,也越来越挤。栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致。
具体而言,栋栋的花的高度可以看成一列整数h_1, h_2, … , h_n。设当一部分花被移走后,剩下的花的高度依次为g_1, g_2, … , g_m,则栋栋希望下面两个条件中至少有一个满足:
条件 A:对于所有的1 g_2i-1,且g_2i > g_2i+1;
条件 B:对于所有的1
注意上面两个条件在m = 1时同时满足,当m > 1时最多有一个能满足。
请问,栋栋最多能将多少株花留在原地。
题解:线段树优化dp
题目中的条件a,b,其实都是等价的,无非都是要求数列是一上一下错排的。
f[i][0]表示选到i是上升趋势的最长序列长度。
f[i][1]表示选到i是下降趋势的最长序列长度。
h[j]
h[j]>h[i] f[i][1]=max(f[i][1],f[j][0]+1)
然后用线段树优化dp
#include
#include
#include
#include
#include
#define N 2000003
using namespace std;
int n,m;
int f[N][3],h[N],tr[N*4],tr1[N*4];
void update(int now)
{
tr[now]=max(tr[now<<1],tr[now<<1|1]);
tr1[now]=max(tr1[now<<1],tr1[now<<1|1]);
}
void change(int now,int l,int r,int x,int v,int v1)
{
if (l==r) {
tr[now]=v;
tr1[now]=v1;
return;
}
int mid=(l+r)/2;
if (x<=mid) change(now<<1,l,mid,x,v,v1);
else change(now<<1|1,mid+1,r,x,v,v1);
update(now);
}
int qjmax(int now,int l,int r,int ll,int rr,int v)
{
if (ll>rr) return 0;
if (ll<=l&&r<=rr) if (!v) return tr[now];
else return tr1[now];
int mid=(l+r)/2;
int ans=0;
if (ll<=mid) ans=max(ans,qjmax(now<<1,l,mid,ll,rr,v));
if (rr>mid) ans=max(ans,qjmax(now<<1|1,mid+1,r,ll,rr,v));
return ans;
}
int main()
{
scanf("%d",&n);
int maxn=0;
for (int i=1;i<=n;i++) scanf("%d",&h[i]),maxn=max(maxn,h[i]);
f[1][1]=f[1][0]=1; change(1,0,maxn,h[1],f[1][0],f[1][1]);
int ans=0;
for (int i=2;i<=n;i++)
{
f[i][0]=qjmax(1,0,maxn,0,h[i]-1,1)+1;
f[i][1]=qjmax(1,0,maxn,h[i]+1,maxn,0)+1;
change(1,0,maxn,h[i],f[i][0],f[i][1]);
ans=max(ans,f[i][0]);
ans=max(ans,f[i][1]);
}
cout<