好题推荐 CF1928D Lonely Mountain Dungeons

题意

有一个军团,其中有 n n n 种士兵,第 i i i 种士兵有 a i a_{i} ai 个,现在要把这些士兵分成 k k k 个军团。定义一个军队的战斗能力如下:

  • 战斗能力初始为 0 0 0
  • 对于每种士兵,战斗能力会增加 不在一个军团 的士兵对的数量乘以 b b b
  • 但是,首领很难操控拥有大量军团的军队,所以战斗能力会减少 x ( k − 1 ) x(k-1) x(k1)
  • 结算时,战斗能力 可能为负

给定 n , b , x n,b,x n,b,x a a a 数组,但 不知道 k k k,求出最大的战斗能力。

题解

Part.1 k k k 确定

一眼看来,可能很难入手,我们先假设 k k k 为定值。那么,我们怎么分配士兵呢?

首先,可以发现,对于增加的战斗力,它等同于所有士兵对的个数减去同一个军团的士兵对数量。假设第 i i i 个军团有 p i p_{i} pi 个士兵,战斗能力等于:

∑ ( a i ( a i − 1 ) − p i ( p i − 1 ) ) 2 \frac{\sum (a_{i}(a_{i}-1)-p_{i}(p_{i}-1))}{2} 2(ai(ai1)pi(pi1))

假设 M = ∑ p i ( p i − 1 ) 2 M=\frac{\sum p_{i}(p_{i}-1)}{2} M=2pi(pi1),现在我们希望 M M M 最小。

假设有 6 6 6 个士兵,要分散在 4 4 4 个军团里, b = 1 , x = 0 b=1,x=0 b=1,x=0。以下有两种分法:

在这里插入图片描述
可以发现,越平均分配, M M M 越小,战斗能力越大。

现在,我们可以写出如下函数 f ( k ) f(k) f(k),表示此时的最大战斗能力:

int f(int mid){
	int cnt=0;
	for (int i=1; i<=n; i++){
		cnt-=(a[i]%mid)*(a[i]/mid+1)*(a[i]/mid)/2;//有 a[i]%mid 个 a[i]/mid+1
		cnt-=(mid-a[i]%mid)*(a[i]/mid)*(a[i]/mid-1)/2;//剩下的全是 a[i]/mid
		cnt+=a[i]*(a[i]-1)/2;
	}
	return cnt*x-(mid-1)*y;//别忘了减少战力(见题意)
}

Part.2 三分答案

详见:三分搜索—百度

此部分只证明是否可以使用三分。

我们可以发现:

  • 如果我们的 k k k 如果太大,那么最后会损失很多战力值,所以不可行。
  • 如果 k k k 太小,那么士兵无法均匀分布在军团内,所以也不好。

得出的结论是: f f f 函数的返回值一定是单峰的,可以使用三分。

Part.3 代码实现

#include <bits/stdc++.h>
using namespace std;
#define int long long 
int n,x,y,a[200010],sum;
int f(int mid){
	int cnt=0;
	for (int i=1; i<=n; i++){
		cnt-=(a[i]%mid)*(a[i]/mid+1)*(a[i]/mid)/2;
		cnt-=(mid-a[i]%mid)*(a[i]/mid)*(a[i]/mid-1)/2; 
		cnt+=a[i]*(a[i]-1)/2;
	}
	return cnt*x-(mid-1)*y;
}
signed main(){
	int t; cin>>t;
	while (t--){
		cin>>n>>x>>y; sum=0;
		for (int i=1; i<=n; i++){
			cin>>a[i]; sum+=a[i];
		}int l=1,r=1000000000;
		while (l<r){
			int m1=l+(r-l)/3;
			int m2=r-(r-l)/3;
			if (f(m1)<=f(m2)){
				l=m1+1;
			}else{
				r=m2-1;
			}
		}cout<<f(l)<<"\n";
	}
} 
  • 28
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值