P1970 [NOIP 2013 提高组] 花匠
首先,我们注意到,每朵花的高度具体数值并不重要,重要的是花与花之间的相对高度大小,所以我们首先可以离散化,把值域变成与\(N\)一个量级。
然后我们设\(gao_i\)为如果以第\(i\)盆花作为结尾,并且\(i\)的高度高于它前面一盆花的情况下,能选上多少盆花。
同理设\(di_i\)表示如果以第\(i\)盆花作为结尾,并且\(i\)的高度低于它前面一盆花的情况下,能选上多少盆花。
那么一个很显然的转移方程就出来了:
\[
\begin{cases}
gao_i=\max\limits_{j=1}^{i-1} [h_j<h_i] \times (di_j+1) \\
di_i=\max\limits_{j=1}^{i-1} [h_j>h_i] \times (gao_j+1) \\
\end{cases}
\]
直接转移肯定是会TLE的,但是既然只需要找满足条件的最大值,我们使用两棵权值线段树分别维护\(gao、di\)的区间最大值。
具体说,假设某个线段树的节点维护的区间是\([l,r]\),区间最大值为\(mx\),那么它就表示高度为\([l,r]\)的所有花中,\(gao_i/di_i\)(若为维护\(di_i\)的线段树此处就为\(di_i\),否则为\(gao_i\))的最大值为\(mx\)。
这样我们就能以\(O(nlogh_{max})\)的时间复杂度通过本题。理论上不离散化的话时间复杂度仍然是正确的,但是线段树常数很大,为了防止TLE呢,还是离散化吧qwq
代码:
P1970
#include<bits/stdc++.h>
#define int long long
#define ls (id<<1)
#define rs (id<<1|1)
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<48){
if(c=='-') f=-1;
c=getchar();
}
while(c>47) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
const int N=1e5+5;
int n,a[N],b[N],gao[N],di[N],ans;
struct sw{
int l,r,mx;
}tr[2][4*N];
inline void build(int id,int l,int r,int op){
tr[op][id].l=l;tr[op][id].r=r;
if(l==r){
tr[op][id].mx=0;
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid,op);build(rs,mid+1,r,op);
tr[op][id].mx=max(tr[op][ls].mx,tr[op][rs].mx);
}
inline void update(int id,int pos,int k,int op){
if(tr[op][id].l==pos&&tr[op][id].r==pos){
tr[op][id].mx=max(tr[op][id].mx,k);
return ;
}
int mid=(tr[op][id].l+tr[op][id].r)>>1;
if(pos<=mid){
update(ls,pos,k,op);
}
else{
update(rs,pos,k,op);
}
tr[op][id].mx=max(tr[op][ls].mx,tr[op][rs].mx);
}
inline int query(int id,int l,int r,int op){
if(l<=tr[op][id].l&&tr[op][id].r<=r){
return tr[op][id].mx;
}
int mid=(tr[op][id].l+tr[op][id].r)>>1;int ans=0;
if(l<=mid) ans=max(ans,query(ls,l,r,op));
if(r>mid) ans=max(ans,query(rs,l,r,op));
return ans;
}
signed main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();b[i]=a[i];
}
build(1,1,n,0);build(1,1,n,1);
//0表示di数组的线段树,1是gao数组的线段树
sort(b+1,b+n+1);
int len=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+len+1,a[i])-b;
}
for(int i=1;i<=n;i++){
gao[i]=query(1,1,a[i]-1,0)+1;
di[i]=query(1,a[i]+1,n,1)+1;
update(1,a[i],gao[i],1);
update(1,a[i],di[i],0);
// ^
// |
//更新a[i]位置上的值
ans=max(ans,max(gao[i],di[i]));
}
printf("%lld",ans);
return 0;
}