【BC】5192 Building Blocks Ⅱ(思维题+树状数组维护)

本文介绍了一种针对积木堆调整问题的算法解决方案,通过计算将积木堆的高度调整到一致所需的最小步骤数。利用树状数组维护区间和,通过枚举不同高度,找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目思路:

首先得明白的是假设高度为h的时候,我们求取最少步数的方法是:max((Hih),(hHj))Hih,Hjh)

其中可以等价于

max(Hicnt(i)h,cnt(j)hHj)
用语言表达的意思就是H1(高于h的所有积木的高度相加之和)-cnt1(高于h的所有积木的堆数)*h
H1(低于h的所有积木的高度相加之和)-cnt1(低于h的所有积木的堆数)*h
的最大值就是把这个w区间的积木堆的高度统一成h的最小步数。
根据题解的描述w区间通过最小的步数所实现的高度就是这个区间所有高度之和的平均值。但是由于这个数可能是小数,所以可能是这个数+1.
而且答案必然是平均值或者平均值+1,但假如平均值比h来的小的话,那就是平均值+1和h进行比较。
接下来的做法便是假设高为h以及平均值+1或者平均值的情况进行分别枚举。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 50005
#define lowbit(x) (x&(-x))
long long Sum[50005];
long long Cnt[50005];
long long high[100005];
long long sum[100005];
long long midS, midC;
long long anh, anc;
void updata(int x,int k)
{
	//为了考虑新堆的情况。需要对零也进行计数
	if (x == 0)
		Cnt[0]+=k;
	else
	for (int i = x; i < MAX; i += lowbit(i))
	{
		Sum[i] += (x*k);
		Cnt[i]+=k;
	}
}

void query(int x)
{
	for (int i = x; i>=0; i -= lowbit(i))
	{
		//考虑新堆要把零的个数计算进去
		midS += Sum[i];
		midC += Cnt[i];
		if (i == 0)
			break;
	}
}

int main()
{
	long long n, w, h;
	while (~scanf("%lld%lld%lld", &n, &w, &h))
	{
		memset(Sum, 0, sizeof(Sum));
		memset(Cnt, 0, sizeof(Cnt));
		memset(high, 0, sizeof(high));
		sum[0] = 0; Cnt[0] = w;
		anh = 50000; anc = 0x7ffffffff;
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &high[i]);
			sum[i] = sum[i - 1] + high[i];
		}
		if (sum[n] <w*h)
		{
			puts("-1");
			continue;
		}
		for (int i = n + 1; i <= n + w; i++)
		{
			sum[i] = sum[i - 1];
		}
		high[0] = 0;
		for (int i = 1; i <= n + w; i++)
		{
			int l = i - w;
			if (l < 0)l = 0;
			//维护w区间的和
			//把每个堆的情况都加到树状数组里面去。
			updata(high[i], 1);
			updata(high[l], -1);
			long long d = sum[i] - sum[l];
			int H = d / (long long)w;
			if (H < h)
				H = h;
			if ((H + 1)*w <= sum[n])
			{
				H++;
				midS = 0;
				midC = 0;
				//得到w区间小于H的全部和。
				query(H);
				//得到w区间大于H的全部和
				d = d - midS;
				long long mid = max(d - (w - midC)*H, H*midC-midS);
				if (anc > mid)
				{
					anh = H;
					anc = mid;
				}
				else
				if (anc==mid)
				if (H > anh)
					anh = H;
				H--;
			}
			midS = 0;
			midC = 0;
			//得到w区间小于H的全部和。
			query(H);
			//得到w区间大于H的全部和
			d = sum[i] - sum[l];
			d = d - midS;
			long long mid = max(d - (w - midC)*H,  H*midC-midS);
			if (anc > mid)
			{
				anh = H;
				anc = mid;
			}
			else
			if (anc == mid)
			if (H > anh)
				anh = H;
		}
		printf("%lld %lld\n", anh, anc);
	}
	return 0;
}



max(Hicnt(i)h,cnt(j)hHj)
max(Hicnt(i)h,cnt(j)hHj)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值