codeforces #334 D. Babaei and Birthday Cake (线段树或者dp+二分)

题目:http://codeforces.com/contest/629/problem/D

题意:给定一个长度为n(n<100000)的序列,求找一个严格升序的子序列使得子序列的和最大。

分析:

定义dp[sum]表示和为sum,以dp[sum]结尾的最小体积。(类似最长上升子序列的做法)

我们发现sum越小,那么dp[sum]就越小,否则,dp[sum]不够优,就不满足定义。

所以dp[sum]数组是具有单调性的。

维护dp[sum]数组的话,用一个set维护。

代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1ll<<62;
const LL MINT = ~0u>>1;
const int maxn = 123456;
struct node
{
	LL dp;
	LL lastValue;
	node(LL a=0,LL b=0):
		dp(a),lastValue(b){}
	bool operator < (const node &nd)const
	{
		return dp<nd.dp;
	}
};

set <node> st;
LL a[maxn],dp[maxn];

void process(LL dp,LL lv)   
{
	/*
	更新set
	若lv比 lv_2 大 ,要插入set的条件是dp>dp_2
	若lv比lv_2小,且dp>dp_2那么set里面的有些节点要被删掉 
	*/
	if(st.empty())
	{
		st.insert(node(dp,lv));
		return ;
	}
	node newnode=node(dp,lv); 
	typedef set <node>::iterator itor;
	itor it = st.lower_bound(newnode);
	if(it==st.end())
	{
		itor temp = it;
		itor per = --temp;
		while(per!=st.begin() && per->lastValue>=lv)
			st.erase(per--);
		if(per==st.begin() && per->lastValue>=lv)
			st.erase(per);
		st.insert(newnode);
	}
	else
	{
		if(it==st.begin())
		{
			if(it->dp==dp)
			{
				if(it->lastValue<=lv)
					return ;
				
				st.erase(it);
				st.insert(newnode);
			}
			else
			{
				if(it->lastValue>lv)
				{
					st.insert(newnode);
				}
				else
				{
					return ;
				}
			}
		}
		else
		{
			if(it->lastValue<=lv)
				return ;
			itor per = it;
			if(it->dp!=dp)
				--per;
			while(per!=st.begin() && per->lastValue>=lv)
					st.erase(per--);
			if(per==st.begin() && per->lastValue>=lv)
				st.erase(per);
			st.insert(newnode);
		}
	}
}
LL Find(LL x)
{
	typedef set <node>::iterator itor;
	LL down=0,mid,up=INF;
	LL ret(0);
	while(down<=up)
	{
		mid=(down+up)/2;
	//	printf("mid:%lld\n",mid);
		itor it = st.lower_bound(node(mid));
		if(it!=st.end() && it->lastValue<x)
		{
			down = mid + 1;
			if(it->dp>ret)
				ret=it->dp;
		}
		else
		{
			up= mid -1;
		}
	}
	return ret;
}

int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int r,h;
		scanf("%d%d",&r,&h);
		a[i]=r*1ll*r*h;
	}
	LL ans(0);
	for(int i=1;i<=n;i++)
	{
		LL f=Find(a[i])+a[i];
		ans=max(ans,f);
		process(f,a[i]);	
	}
	printf("%.10lf",ans*1.0*acos(-1));
	return 0;
}



解法二

分析:将体积离散化之后,作为线段树的节点编号,每个节点存区间的最大体积和就ok了,容易理解,代码又好敲。

代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1ll<<62;
const LL MINT = ~0u>>1;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int maxn = 2e5;
LL a[maxn],uniq[maxn];       //体积 
LL tree[maxn<<2];   //线段树维护体积和 

void update(int pos,LL v,int l,int r,int rt)
{
	if(l==r)
	{
		tree[rt]=max(tree[rt],v);
		return ;
	}
	int m=(l+r)>>1;
	if(pos<=m)
		update(pos,v,lson);
	else
		update(pos,v,rson);
	tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
LL query(int L,int R,int l,int r,int rt)
{
	if(L<=l && r<=R)
		return tree[rt];
	int m=(l+r)>>1;
	LL ret(-INF);
	if(L<=m)
		ret=max(ret,query(L,R,lson));
	if(R>m)
		ret=max(ret,query(L,R,rson));
	return ret;
}

int Find(LL f,int n)
{
	int down=1,mid,up=n;
	while(down<=up)
	{
		mid = (down+up)>>1;
		if(uniq[mid]==f)	return mid;
		if(uniq[mid]>f)	up=mid-1;
		else	down=mid+1;
	}
	return -1;
}

int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int r,h;
		scanf("%d%d",&r,&h);
		uniq[i]=a[i]=r*1ll*r*h;
	}
	sort(uniq+1,uniq+1+n);
	int tail=unique(uniq+1,uniq+n+1)-uniq-1;
	LL ans(0);
	for(int i=1;i<=n;i++)
	{
		int index=Find(a[i],tail);
		LL sum(a[i]);
		if(index>1)
			sum+=query(1,index-1,1,n,1);
		ans=max(ans,sum);
		update(index,sum,1,n,1); 
	}
	printf("%.10lf\n",ans*acos(-1));
	return 0;
}







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值