luogu P1873 [COCI 2011/2012 #5] EKO / 砍树

这篇博客介绍了一个利用二分查找优化的算法来解决树状数组问题,旨在找到最大的正整数,使得所有高度超过该数的树的高度之和不小于给定阈值。博主首先对树的高度进行排序,然后采用二分搜索确定答案,并进一步优化了求解“从哪开始锯”的部分,将时间复杂度降低到O(NlogN)。
摘要由CSDN通过智能技术生成

题目大意

有正整数 N , M N,M N,M N N N 棵树,第 i i i 棵树的高度为正整数 a i a_i ai。求一个最大的正整数 a n s ans ans,使得所有树中高度超过 a n s ans ans 的部分的高度之和不小于 M M M

解题思路

一种显然的思路是,二分枚举 a n s ans ans,再分别检验答案是否合理。为了检验便捷,需对 a i a_i ai 排序。时间复杂度 O ( N log ⁡ 2 N + log ⁡ 2 M ) O(N\log_2 N+\log_2 M) O(Nlog2N+log2M)。病历本:类型转换。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

const int MAXN=1000010;
typedef long long ll;

int n, m;
int e[MAXN];
int bk[MAXN], d[MAXN];
ll sum[MAXN];

inline int read (){
	int s=0; char c;
	do c=getchar (); while ('0'>c||'9'<c);
	while ('0'<=c&&'9'>=c)
		s=s*10+c-'0', c=getchar ();
	return s;
}
int main(){
	n=read (); m=read ();
	for (int i=1; i<=n; ++i)
		e[i]=read ();
	std::sort (e+1, e+n+1);
	//discretize
	int cnt=0, last=-1;
	for (int i=1; i<=n; ++i){
		if (e[i]!=last) ++cnt;
		++bk[cnt]; d[cnt]=e[i];
		last=e[i];
	}
	sum[0]=0; e[0]=0;
	for (int i=1; i<=n; ++i)
		sum[i]=e[i]+sum[i-1];
	//锯掉哪些树 
	int left=1, right=n, mid, ans=-1;
	while (left<=right){
		mid=(left+right)/2;
		if (sum[n]-sum[mid]-(ll)(n-mid)*(ll)e[mid]>=m)
			ans=mid, left=mid+1;
		else right=mid-1;
	}
	//从哪锯 
	left=e[ans]; right=e[ans+1]-1;
	ll count=sum[n]-sum[ans];
	int length=n-ans; ans=-1;
	while (left<=right){
		mid=(left+right)/2;
		ll tmp=count-(ll)mid*(ll)length;
		if (tmp>=m)
			ans=mid, left=mid+1;
		else right=mid-1;
	}
	printf ("%d", ans);
}

如果不枚举从哪棵树开始锯,则时间复杂度变为 O ( N log ⁡ 2 N + log ⁡ 2 N log ⁡ 2 M ) O(N\log_2N+\log_2N\log_2M) O(Nlog2N+log2Nlog2M),同样可以接受。

“从哪开始锯"的部分也可以通过数学计算算出。则时间复杂度变为 O ( N log ⁡ 2 N ) O(N\log_2N) O(Nlog2N)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值