CF1661C Water the Trees

Link\text{Link}

题意

有一个 nn 个数的整数序列 h1nh_{1\sim n},你可以花总共 kk 天时间,在第 xx 天进行如下操作之一:

  • xx 为奇数,将任意一个 hih_i 增加 11
  • xx 为偶数,将任意一个 hih_i 增加 22
  • 啥也不干。

kk 最小为多少时存在一种操作方法可以使所有 hih_i 相等。

分析

mx=maxi=1nhimx=\max_{i=1}^nh_i,那么答案的方案中所有 hih_i 最后都会变成 mxmxmx+1mx+1 之一,显然更大的 mxmx 都不如这两个优。

设最后所有的 hih_i 都变成了 m{mx,mx+1}m\in\{mx,mx+1\},那么问题就转化为了求最小的 kk,每天按照规则将 di=mhid_i=m-h_i 减少使得最后所有的 did_i 都等于 00

显然 kk 满足单调性,即 kk 小于答案时无法使所有 di=0d_i=0,大于等于时则可以,因此我们可以二分答案转求解为判定,而判定相当容易,设二分到 xx,那么我们就有 x+12\left\lfloor\dfrac{x+1}{2}\right\rfloor11x2\left\lfloor\dfrac{x}{2}\right\rfloor22 进行操作,根据贪心的想法,能用 22 就用 22,实在不行再用 11,如果走投无路了则说明无法满足要求。

值得注意的是,二分的 rr 不要开太小,至少要到 1+2×i=1ndi1+2\times \sum_{i=1}^n d_i,不然会 WA;而且每次二分时要重置 dd 数组。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
	long long x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void write(long long x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int N=3e5+10;
int t,n;
ll h[N],d[N],ans,mx,l,r;
ll od,ed;//奇偶 
bool check(ll x){
	od=(x+1)>>1;
	ed=x>>1;
	for(int i=1;i<=n;i++){
		d[i]=mx-h[i];
		if(ed*2>=d[i]){
			ed-=d[i]/2;
			d[i]=d[i]%2;
		}
		else{
			d[i]-=ed*2;
			ed=0;
		}
		if(d[i]>od)
			return 0;
		od-=d[i];
	}
	return 1;
}
int main(){
	t=read();
	while(t--){
		n=read();
		mx=0;
		for(int i=1;i<=n;i++){
			h[i]=read();
			mx=max(mx,h[i]);
		}
		//第一次二分 m=mx 
		l=0;r=0;
		for(int i=1;i<=n;i++)
			r+=mx-h[i];
		r*=2;r++;
		while(l<r){
			ll mid=(l+r)>>1;
			if(check(mid))
				r=mid;
			else
				l=mid+1;
		}
		ans=l;
		//第二次二分 m=mx+1 
		mx++;
		l=0;r=0;
		for(int i=1;i<=n;i++)
			r+=mx-h[i];
		r*=2;r++;
		while(l<r){
			ll mid=(l+r)>>1;
			if(check(mid))
				r=mid;
			else
				l=mid+1;
		}
		printf("%lld\n",min(ans,l));
	}
	return 0;
}
posted @ 2022-04-11 23:08  luckydrawbox  阅读(9)  评论(0)    收藏  举报  来源