洛谷P1020 导弹拦截 树状数组区间求最值

思路:

  1. 求最多能拦截多少导弹是求最长不升子序列
  2. 求配备多少套这种系统是求最长下降子序列
  3. 题目要求做法为 O ( log ⁡ n ) O(\log{n}) O(logn)
  4. 题目的本质是最值的区间查询

举例:
序列: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 
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;
}

你可能感兴趣的:(题解,数据结构)