洛谷 P1020 导弹拦截

题目链接

0x00 朴素DP

对于第一问,我们求最长不上升子序列,因为高度不超过

对于第二问,我们求最长上升子序列。证明:

百度百科---狄尔沃斯定理 Dilworth定理

解释一下反链:

假设原偏序集合任意两相邻元素的偏序关系为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;
}
posted @ 2020-03-28 10:54  zhuzihan  阅读(97)  评论(0编辑  收藏  举报