cf343D dp+线段树优化

好久以前做过这题,想的方法不够优秀,这次打cf又碰到了,看到了题解,终于理解了

首先这种题目第一感觉就是dp或者贪心
设dp[I] 表示从i开始的最大重量和
要求的是最大上升序列和
最后maxdp[I] i从1到n就是结果

关键就在于状体转移的时候如何快速找到需要的状态dp[I] = max{dp[j]  v[j] >v[i] j > i}

要找到后面的圆柱体中体积比他大的中总体积最大的,我的一个思路是:比如从i+1开始找,找到一个v[j]>v[i] 那么后面就应该找v[k]<v[j]的,然后做初始化……这样利用了dp的性质减少了部分计算,但是复杂度没有变化

下面介绍用线段树优化的方法
首先做一个排序,得到每个v[i]在排序后序列中的位置p[i](应该去掉一样大的元素)
这样每次只要找p[i]前面的元素,并且总体积已经算过的,最大值就可以了
线段树初始化的时候每个都是0,算过总体积的,就更新线段树,这样,每次找的范围是一定的,就能利用线段树再logn时间完成转移
总复杂度为nlogn

应该想到排序,然后找前面的中的最值,就可以联系到线段树了!排序这种东西,不排白不排

上AC代码,居然1A,神奇
#include<stdio.h>
#include<string.h>
#include<iostream> 
#include<algorithm>
using namespace std;
const double pai = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384;
typedef long long LL;
LL r,h;
LL v[100005];
struct cy
{
	LL v;
	LL rank;
}a[100005];
bool compare(cy x,cy y)
{
	return (x.v < y.v);
}
int b[100005];//从1- (b【i】 - 1 )找最大值 
LL seg[500000];
int c[100005];
int n;
void init(int n_)
{
	n = 1;
	while(n < n_) n*=2;
	for(int i = 0; i < 2 * n - 1; i++)seg[i] = 0;
} 
 
 void update(int k,LL a)//k也从0开始 
 {
     k += n - 1;
	 seg[k] = a;
	 while(k > 0){
		k = (k - 1)/2;
		seg[k] = max(seg[k*2+ 1],seg[k * 2 + 2]);
	}
}


LL query(int a,int b,int k,int l,int r){
	if (r <= a || b <= l) return 0;
	if (a <= l && r <= b) return seg[k];
	else{
		LL vl = query(a,b,k*2+1,l,(l + r)/2);
		LL vr = query(a,b,k*2+2,(l + r)/2,r);
		return max(vl,vr);
	}
}

LL dp[200000];



int main()
{
	int nn;
	cin >> nn;
	for(int i = 0; i < nn; i++){
	    cin >> r >> h; 
	    a[i].v = r * r * h;
		a[i].rank = i;
		v[i] = a[i].v;	
	}
	sort(a , a + nn,compare);
    
	b[a[nn - 1].rank] = nn - 1;
	c[a[nn - 1].rank] = nn - 1;
	int tem = 1;
	for(int i = nn - 2; i >= 0; i--){
		if (a[i].v == a[i + 1].v){
			b[a[i].rank] = b[a[i + 1].rank];
			tem++;
		}
		else {
			b[a[i].rank] = b[a[i + 1].rank] - tem;
	        tem = 1;
	    }
	    c[a[i].rank] = i;
	}
	
	init(nn);
	
	for(int i = nn - 1; i >= 0; i--){
		//cout << v[i] << ' ' << b[i] << ' ' << c[i] << ' ';
		dp[i] = 0;
		dp[i] = query(b[i] + 1,n,0,0,n) + v[i];
		update(c[i],dp[i]);
	    //cout << dp[i] << '\n';
	}
	LL ans = 0;
    for(int i = 0; i < nn; i++)
        ans = max(dp[i],ans);
	printf("%lf\n",pai*ans); 
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值