思路:
- 求最多能拦截多少导弹是求最长不升子序列
- 求配备多少套这种系统是求最长下降子序列
- 题目要求做法为O(logn)O(\log{n})O(logn)
- 题目的本质是最值的区间查询
举例:
序列:389 207 155 300 299 170 158 65
最值(向上):1 2 3 2 3 4 5 6
最值(向下):1 1 1 2 2 2 2 1
我们只需要贪心的选取之前的最值即可
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+500;
int tot,a[maxn];//原数组
int t[maxn],mx; //树状数组与上边界
int query1(int x){ // max(x,...,mx)
int res = 0;
for(int i=x;i<=mx;i+=i&-i)
res = max(res,t[i]);
return res;
}
void update1(int x,int val){
for(int i=x;i;i-=i&-i)
t[i] = max(t[i],val);
}
int query2(int x){
int res = 0;
for(int i=x;i;i-=i&-i)
res = max(res,t[i]);
return res;
}
void update2(int x,int val){
for(int i=x;i<=mx;i+=i&-i)
t[i] = max(t[i],val);
}
void solve1(){ //最长不升子序列
int res = 0;
for(int i=0;i<tot;i++){
int now = query1(a[i])+1;
res = max(res,now);
update1(a[i],now);
//不是update1(a[i]+1,now),因为自己的值也能贪心的选取
}
printf("%d\n",res);
}
void solve2(){ //最长下降子序列
memset(t,0,sizeof(t));
int res = 0;
for(int i=0;i<tot;i++){
int now = query2(a[i])+1;
res = max(res,now);
update2(a[i]+1,now);
}
printf("%d\n",res);
}
int main(){
while(scanf("%d",&a[tot])!=EOF)
mx = max(mx,a[tot++]);
solve1(); //求最长不升子序列
solve2(); //求最长下降子序列
return 0;
}