2019 沈阳网络赛 Honk's pool(浩克的池子)(二分答案)

博客讲述了如何解决一个关于浩克的池子的问题,其中涉及每日操作包括从水最多的池子取水,向水最少的池子注水。通过计算k天后泳池水量的极差,利用平均值和二分法来确定可能的最大最小值变化,以优化求解过程。

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

As we all know, Honk has nn pools, numbered as 11 ~ nn . There is a_iai​ liters water in the ii-th pool. Every day, Honk will perform the following operations in sequence.

我们都知道,浩克有n个池子,以1~n标号,在第i个池子里有ai升水,每一天,浩克都会依序重复下列操作:

  1. Find the pool with the most water (If there are more than one, choose one at random) and take one liter of water.(找到水最多的一个池子(如果有多个水最多的池子,任意选择一个),从中取一升水)

  2. Find the pool with the least water (If there are more than one, choose one at random) and pour one liter of water into the pool.(找到水最少的一个池子(如果有多个水最少的池子,任意选择一个),把那一升水倒入其中)

  3. Go home and rest (Waiting for the next day).(回家睡觉等待下一天)

Please calculate the difference between the amount of water in the pool with the most water and the amount of water in the pool with the least water after the kk days.

请计算出k天后的泳池水量的极差。

首先求出全体水量的平均值,这个平均值意味着最小值的向上最大范围和最大值的向下最小范围。如果平均值不能整除,则令最大值的向下最小范围加一即可。

在n个池子中,转移量是一定的,也就是开头给出的k,而要求这些转移量可以对高于平均值和低于平均值的池子的极值造成什么样的影响,就可以用二分求出。具体操作就是二分到某个值a时,就遍历数组,用比它大的值减去a(如果求最小值的上界,那就去用a减数组元素),就可以得到把最大值降到a所需的转移量,最后与k比较即可

  1. 比如样例 4 100 1 1 10 10
  2. 可以看出所有水池的最终状态是5 5 6 6
  3. 最大最小值都改变的第一个状态是2 2 9 9,所需转移量为2,k满足这个大小
  4. 最大最小值都改变的第二个状态是3 3 8 8,所需转移量为4,k满足这个大小
  5. …………
  6. 最终状态是5 5 6 6,所需转移量是8,k满足此大小

 如上过程所述,如果在中间出现不满足的情况直接跳出,在二分里依据情况重新划分区间查询即可。这些转移量都是使得水池的最大最小值足以发生改变的数值,不会出现使得水池水量为2 1 9 10这样情况的转移量。

值得一说的是,对枚举范围进行确定可以对时间效率上进行优化,比如说最大值的上下界是【平均值,数组最大值】,最小值的上下界时【数组最小值,平均值】

#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug cout<<"procedures above are available"<<endl;
#define BigInteger __int128
const int INF = 2e9 + 2;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
//    BigInteger tmp = 0, si = 1;char c;    c = getchar();
//    while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
//    while (isdigit(c))
//    {tmp = tmp * 10 + c - '0';c = getchar();}
//    return si * tmp;    
//}            
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
//    if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
//    return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
//    if (x < 0)
//    {x = -x;putchar('-');}
//    if (x > 9) output(x / 10);
//    putchar(x % 10 + '0');
//    }
/**Maintain your determination.Nobody knows the magnificent landscape
at his destination before the arrival with stumble.**/
/**Last Remote**/
ll arr[234234];
bool check(ll curstatus, ll reality, ll limit, bool sign = false)
{
	ll tmpcost = 0;
	for (int i = 1; i <= limit; i++)
	{
		if (arr[i] >= curstatus && sign == false)
			tmpcost += arr[i] - curstatus;
		else if (arr[i] <= curstatus && sign == true)
			tmpcost += curstatus - arr[i];
	}
	if (tmpcost > reality)
		return false;
	else
		return true;
}
int DETERMINATION()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0), std::cout.tie(0);
	ll n, k;
	while (std::cin >> n >> k)
	{
		ll sum = 0, mn = INF, mx = 0;
		for (int i = 1; i <= n; i++)
		{
			std::cin >> arr[i];
			sum += arr[i];
			mx = std::max(arr[i], mx);
			mn = std::min(arr[i], mn);
		}
		ll lower = 0, upper = 0;
		if (sum%n == 0)
		{
			lower = sum / n;
			upper = sum / n;
		}
		else
		{
			lower = sum / n + 1;
			upper = sum / n;
		}
		//std::cout << lower << "%$^" << upper << std::endl;
		ll finlower = 0, finupper = 0;
		ll tmplower = lower, tmpupper = mx;
		while (tmplower <= tmpupper)
		{
			ll mid = (tmplower + tmpupper) >> 1;
			if (check(mid, k, n, false))
			{
				tmpupper = mid - 1;
				finupper = mid;
			}
			else
				tmplower = mid + 1;
		}
		//std::cout << finupper << std::endl;
		tmplower = 0;
		tmpupper = upper;
		while (tmplower <= tmpupper)
		{
			ll mid = (tmplower + tmpupper) >> 1;
			if (check(mid, k, n, true))
			{
				finlower = mid;
				tmplower = mid + 1;
			}
			else
				tmpupper = mid - 1;
		}
		//std::cout << finupper << " " << finlower << std::endl;
		std::cout << finupper - finlower << std::endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值