洛谷 P1020 导弹拦截
0x00 朴素DP
对于第一问,我们求最长不上升子序列,因为高度不超过
对于第二问,我们求最长上升子序列。证明:
解释一下反链:
假设原偏序集合任意两相邻元素的偏序关系为P,反链的相邻元素偏序关系为!P.
最长上升 < -- > 最长不上升
最长下降 < -- > 最长不下降
然后朴素DP:
\(f[i] = max(f[j], 0) + 1 ( j < i, a[i] \leq a[j])\)
\(f[i] = max(f[j], 0) + 1 ( j < i, a[i] > a[j])\)
见代码注释部分
0x01 优化
显然,\(a\)数组/\(f\)数组/下标\(i\),构成偏序关系
下标已经有序
线段树维护另外两维即可
[Code]
#include<bits/stdc++.h>
using namespace std;
int n;
int a[100005];
int f[100005];
int ans;
#define mid ((l + r) >> 1)
#define ls (nod << 1)
#define rs (nod << 1 | 1)
#define lson ls,l,mid
#define rson rs,mid + 1,r
int t[250005];
void build(int nod,int l,int r){
t[nod] = 0;
if(l == r) return ;
build(lson); build(rson);
}
void update(int nod,int l,int r,int ps,int v){
t[nod] = max(t[nod],v);
if(l == r) return ;
if(ps <= mid) update(lson,ps,v); else update(rson,ps,v);
}
int query(int nod,int l,int r,int ll,int rr){
if(l > rr || r < ll) return 0;
if(ll <= l && r <= rr) return t[nod];
return max(query(lson,ll,rr),query(rson,ll,rr));
}
signed main(){
int tmp;
while(scanf("%d",&tmp) != EOF){
a[++ n] = tmp;
}
f[1] = 1; ans = 1; build(1,1,50000); update(1,1,50000,a[1],f[1]);
for(int i = 2; i <= n; ++ i){
f[i] = max(query(1,1,50000,a[i],50000) + 1,1);
// for(int j = 1; j < i; ++ j)
// if(a[i] <= a[j]) f[i] = max(f[i],f[j] + 1);
ans = max(ans,f[i]); update(1,1,50000,a[i],f[i]);
}
printf("%d\n",ans);
f[1] = 1; ans = 1; build(1,1,50000); update(1,1,50000,a[1],f[1]);
for(int i = 2; i <= n; ++ i){
f[i] = max(query(1,1,50000,1,a[i] - 1) + 1, 1);
// for(int j = 1; j < i; ++ j)
// if(a[i] > a[j]) f[i] = max(f[i],f[j] + 1);
ans = max(ans,f[i]); update(1,1,50000,a[i],f[i]);
}
printf("%d\n",ans);
return 0;
}