最大权值(树状数组/线段树优化dp)

第1题     最大权值 查看测评数据信息

小明在校园里种了n棵树排成一排,第i棵树有两个属性,高度h[i],价值a[i],保证每棵树的高度不同,现在学校要砍伐一些树,使得剩余的树高度单调递增,并且剩余的树价值最大,问价值最大是多少。

输入格式

 

第一行一个整数n

第二行n个整数,表示h[1],h[2],...h[n]

第三行n个整数w[1],w[2],...w[n]

1<=n<=2e5,1<=h[i]<=n,1<=w[i]<=1e9

 

输出格式

 

一个整数

 

输入/输出例子1

输入:

4

3 1 4 2

10 20 30 40

 

输出:

60

 

输入/输出例子2

输入:

1

1

10

 

输出:

10

 

样例解释

 

 

dp优化:如果dp根据前一段dp值进行更新,然后要对当前点更新,也就是 dp[i]=dp[i-1]+.....,然后有限制条件,例如 a[i]<a[j],才能让i的值给j,考虑线段树/树状数组进行优化,下标对应的就是a的最大值,存的值就是最优的dp[i]

 

类似最长上升子序列,但是朴素dp,O(n^2)肯定会炸,这里放出朴素dp:

定f[i]:前i个数,留下的树高度单调上升,留下的树的价值的和的最大值

我们可以枚举一个j,j从1~i-1,限制:h[j] < h[i]

满足限制后,转移就是  f[i]=max(f[i], f[j]+w[i])

初始化:从第i棵树往后开始选,前面的一律不管了,也就是 f[i]=w[i]

答案就是1~n的最大f[i]

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;

int n, h[N], w[N];
long long f[N], ans=0;
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%d", &h[i]);
	for (int i=1; i<=n; i++) scanf("%d", &w[i]);
	
	for (int i=1; i<=n; i++)
	{
		f[i]=w[i];
		for (int j=i-1; j>=1; j--)
			if (h[j]<h[i]) f[i]=max(f[i], w[i]+f[j]);
	}
	
	for (int i=1; i<=n; i++) ans=max(ans, f[i]);
	printf("%lld", ans);
	return 0; 
} 
/*
7
1 5 2 6 3 7 4
60 80 20 100 90 40 20

7
1  5  2   6  3   7 4
60 80 20 100 90 40 20

280
*/

  

发现这里无法对 i 这里做优化,那么只能对 j 做文章了

我们想要找到满足限制的前i-1个的f[j]的最大值,找到后对第i位修改

转换为区间查询,单点修改,线段树和树状数组都是可以的

这里用树状数组。

树状数组存的肯定是 f 的最大值嘛

不过这里下标对应的值将会有意义,下标的意义变成的树的高度(并且树的高度不是很大),那么这样转换,问题就迎刃而解了。

我们只需要找 1~h[i]-1 的区间内的最大值就是区间查询了。

那么单点修改也很简单了,把下标为 h[i] 的值更新一下最大值为 f[i]

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;

int n, h[N];
long long f[N], ans=0, w[N], s[N];
int lowbit(int x)
{
	return x&(-x);
}
void add(int x, long long y)
{
	for (int i=x; i<=n; i+=lowbit(i)) s[i]=max(s[i], y);
}
long long query(int x)
{
	long long res=0;
	for (int i=x; i>=1; i-=lowbit(i)) res=max(s[i], res);
	return res;
}
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%d", &h[i]);
	for (int i=1; i<=n; i++) scanf("%lld", &w[i]);
	
	for (int i=1; i<=n; i++)
	{
		f[i]=max(w[i], w[i]+query(h[i]-1));
		add(h[i], f[i]);
	}
	
	for (int i=1; i<=n; i++) ans=max(ans, f[i]);
	printf("%lld", ans);
	return 0; 
} 
/*
7
1 5 2 6 3 7 4
60 80 20 100 90 40 20

7
1  5  2   6  3   7 4
60 80 20 100 90 40 20

280
*/

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2024-08-22 19:41  cn是大帅哥886  阅读(47)  评论(0)    收藏  举报