1832D1

Red-Blue Operations (Easy Version)

题目出处

Problem - D1 - Codeforces

题目大意

给定一个长度为 \(n\) 的序列,每个位置都有一个整数以及一个颜色,颜色最初都是红色。

可以在这个序列上进行操作,\(i\) 次操作选定第 \(j\) 个数

如果位置 \(j\) 是红色,该位置的数加 \(i\) ,颜色变为蓝色。

如果位置 \(j\) 是蓝色,该位置的数减 \(i\) ,颜色变为红色。

给定 \(q\) 次独立的询问,每次询问要求通过恰好 \(k\) 次操作最大化序列中的最小值并输出。

知识点、思路

二分,贪心,分类讨论

具体想法、推导

看到 最大化序列中的最小值 可以比较轻易想到二分+验证的方法,即二分答案,然后验证答案是否成立,再缩小答案区间。所以问题就转化成了如何验证答案是否成立。

考虑到一个数在进行一次加操作后、下次若对它操作一定是减操作,并且由于操作编号的递增,减小的值比增加的值还要大。所以可以想到,对于需要增加一些值的数,可以让对它的增加操作尽可能靠后,如果有 \(r\) 个数需要增加,我们可以算出它们离目标值的距离并排序,并且用最后 \(r\) 次操作依次给它们增加一些值。

如果可以通过这种方法使得所有数都满足条件,那么还需要考虑剩余的操作是否能被成功地消耗掉,从而不会影响最后的过程。

如果没有剩余操作,那么显然成功。

如果序列中每个数都需要增加的情况:

1.如果剩余操作为奇数,那么可以发现至少有一个数需要被减小,一定不成功

2.如果剩余操作为偶数,计为 \(u\) ,那么序列就需要先减少 \(u/2\) 再增加。只需要判断是否有多余的 \(u/2\) 供序列先减少。

如果序列中存在高于目标值的数:

1.如果剩余操作数为奇数,那么使这个值也增加就一定成功

2.如果剩余操作数为偶数,计为 \(u\) ,如果高于目标数的数大于等于二,那么将这两个值都通过奇数次操作增加即可。如果只有一个高于目标数的数,需要判断是否有多余的 \(u/2\) 供序列先减少。

示例代码

bool check(ll x,ll k){
	ll cnt=0,op=0,better=0;
	vector<ll> need;
	need.pb(0);
	for (int i=1;i<=n;i++){
		if (a[i]<x){
			need.pb(x-a[i]);
			cnt++;
		}
		else op=a[i]-x;
	}
	if (k<cnt) return false;
	ll r=k-cnt;
	sort(need.begin(),need.end());
	for (int i=1;i<=cnt;i++){
		if (i+k-cnt<need[i]) return false;
		better+=i+k-cnt-need[i];
	}
	if (r==0) return true;
	if (n==cnt) {
		if (r%2==1) return false;
		if (better<r/2) return false;
		return true;
	}
	if (r%2==1) return true;
	if (n-cnt==1){
		return op+better-r/2>=0;
	}
	return true;
}
 
void solve()
{	
	cin>>n>>q;
 
	for (int i=1;i<=n;i++) cin>>a[i];
	for (int i=1;i<=q;i++) cin>>b[i];
 
	for (int i=1;i<=q;i++){
		ll k=b[i];
 
		ll l=-1e9,r=2e9;
 
		while (l<r){
			ll mid=(l+r+1)/2;
			if (check(mid,k)) l=mid;
			else r=mid-1;
		}
		
		cout<<l<<' ';
	}
	cout<<endl;
}
posted @ 2023-05-19 16:23  小孙孙得到  阅读(41)  评论(0)    收藏  举报