11.11新生赛(2021)G题AT2580 Widespread整理[★★★★★]
[ARC075B] Widespread - 洛谷https://www.luogu.com.cn/problem/AT2580?contestId=56578前排提醒
本题的数据范围比较大,全用long long最好
否则会有一部分点会WA
我们每一次攻击,都有一个怪兽受到A点伤害,其余怪兽受到B点波及伤害
设怪兽总数为n 一共进行了x次攻击
t[i]为第i只怪兽受到的中心爆炸攻击的次数
则这只怪兽所受伤害总量为 A*t[i]+(x-t[i])*B = B*x +(A-B)*t[i]
且 x=t[1]+t[2]+...+t[n]
所有怪兽都会受到B*x点伤害!
先将所有怪兽从小到大排序
sort(a,a+n)
采用二分的方式来寻找攻击的次数x
攻击次数最少是1 所以二分的左边界left=1
右边界先初始化为0
每一次输入a[i]时 right+=x/b+1 //也就是假设最坏情况,这只怪兽总是受到波及伤害 不管波没波及死都+1.波及死了,加1不影响结果. 没波及死,再中心攻击一次就死了
为什么总是受到波及伤害就是最坏情况呢.
因为题目假定A>B
如果任有怪物受到中心伤害的,肯定比全部怪物都受波及伤害所需的攻击次数要少.因为a[i]/a < a[i]/b,那总和自然也小了
(注意二分的左边界不能用每个怪物血量除以A的和来计算,因为有波及的存在,可能并不需要那么多次数就会死)
找到了二分的左右边界
就可以开始二分了,我们二分的就是攻击的总次数
while(left<right)
{
if(check(mid))
{
right=mid;
}
else left=mid+1;
mid=(left+right)/2;
//根据yxc的二分模板
//left+1 时 mid=(left+right)/2;
//right-1 时 mid=(left+right+1)/2;
//这里显然是前者
}
然后我们来写二分的check函数
想求出每一个怪物所受的攻击次数是很难的,所以我们换个思路
只假定攻击次数为x,验证能否消灭所有怪物.
每只怪物必定受到x*B点伤害
所以第[i]只怪物的血量m[i]=m[i] - x*B
如果这只怪物剩余血量小于等于0 ,那就不用看了,直接被波及死,不计入攻击次数x
如果大于0 根据我们前面得出的那个式子
这只怪兽所受伤害总量为 A*t[i]+(x-t[i])*B = B*x +(A-B)*t[i]
而 t/(A-B) 就是要对第i只怪物进行攻击的次数t[i]
当然,如果剩余血量能整除(A-B) 那攻击次数正好为剩余血量/(A-B)
否则需要再+1 (再多一次中心攻击)
从血量最大的开始遍历
int check(long long int x)// 当总共发起x次攻击时
{
long long int sum=0;
for(int i=n-1;i>=0;i--)
{
long long int t=m[i]-x*b;//受到所有波及伤害后剩余血量
//小于0就不用管了 直接被波及死
if(t>0)
{
if(t%(a-b)==0) sum+=t/(a-b);
else sum+=t/(a-b)+1;
}
}
if(sum>x) return 0;
return 1;
}
最后献上完整AC代码
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<string.h>
#include<algorithm>
using namespace std;
long long int m[1000010];
long long int n;
long long int a,b;
int check(long long int x)// 当总共发起x次攻击时
{
long long int sum=0;
for(int i=n-1;i>=0;i--)
{
long long int t=m[i]-x*b;//受到所有波及伤害后剩余血量
//小于0就不用管了 直接被波及死
if(t>0)
{
if(t%(a-b)==0) sum+=t/(a-b);
else sum+=t/(a-b)+1;
}
}
if(sum>x) return 0;
return 1;
}
int main()
{
//freopen("uva.txt","r",stdin);
cin>>n>>a>>b;
long long int left=1;
long long int right=0;
for(int i=0;i<n;i++)
{
long long int x;
scanf("%lld",&x);
m[i]=x;
right+=x/b+1;
}
right+=1;
sort(m,m+n);
long long int mid=(left+right)/2;
while(left<right)
{
if(check(mid))
{
right=mid;
}
else left=mid+1;
mid=(left+right)/2;
}
printf("%lld",right);
return 0;
}
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现