博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[洛谷1485] 火枪打怪
阅读量:5193 次
发布时间:2019-06-13

本文共 3615 字,大约阅读时间需要 12 分钟。

题目描述

LXL进入到了一片丛林,结果他发现有n只怪物排成一排站在他面前。LXL有一杆火枪能对付这些怪物。他知道从左至右数第i只怪物的血量是mi。现在LXL可以将一些子弹射向某个怪物。LXL可以控制他所发射的子弹数量及子弹的威力值。当某个子弹射到第i个怪物,如果这个子弹的威力值为p,除了这个怪物会掉p点血以外,它左边的第j个怪物(j<i),也会遭到Max(0, p - (i - j) * (i - j))的溅射伤害(好神奇的子弹)。当某只怪物的血量小于0时,它就死了,但它的尸体还在,即怪物的位置永远不会改变。LXL希望只用k发子弹,请你求出一个最小的正整数p,使LXL用k发子弹且每发子弹的威力值为p就可以消灭所有怪物。

输入输出格式

输入格式:

第一行,两个正整数n,k。

第二行,n个正整数,第i个正整数表示从左至右数第i只怪物的血量mi。

输出格式:

一个正整数,表示子弹的最小威力值p。

输入输出样例

输入样例#1:
3 11 4 5
输出样例#1:
6

说明

对于30%的数据,n<=300。

对于100%的数据,n<=500000,k<=500000,1<=mi<=10^10

题目要求

LXL面前有n只怪物,他将一些子弹射向某个怪物。LXL可以控制他所发射的子弹数量及子弹的威力值。当某个子弹射到第i个怪物,如果这个子弹的威力值为p,除了这个怪物会掉p点血以外,它左边的第j个怪物(j<i),也会遭到Max(0, p - (i - j) * (i - j))的溅射伤害。当某只怪物的血量小于0(敲黑板!!等于0还没有死喔)时,它就死了,但是位置永远不会改变。求出用k发子弹消灭所有怪物的最小威力值p,且p为正整数。

思路分析

思路和楼下基本是相同的,另外扩充一些细节。

首先提醒一个细节:会爆int,所以定义要用longlong。

其次对做法进行分析。因为p是跟怪物的Hp值相关的,所以我们先观察一下Hp的范围:1<=Hp[i]<=10^10。因为范围比较大,所以我们采用二分的方法来寻找最优解。需要注意的是,威力值p有可能是大于Hp值的,所以我们在对p进行二分查找的时候要将范围在[1,10^10]的基础上再进行扩大。

那么如何判断p是否满足题目条件呢?这里我们写一个判断函数对其进行条件满足与否的判定。如何判定是整个题的关键,那么我们一步一步来分析:

<判断函数>

子弹的溅射范围是在被击中怪物的左侧,所以为了尽可能的多溅射到怪物,我们从最右边开始枚举。我们用一个num数组来记录每个位置上射出子弹的个数(受到伤害为p而不是p-(i-j)*(i-j)的子弹个数,也就是说被溅射到的不算,溅射的子弹会用别的方法储存)。接下来怎么做呢?

首先我们对式子p-(i-j)*(i-j)进行处理。将该式拆开得到:伤害值=p-i^2+2ij-j^2。由于我们在枚举时p的值在该次枚举内是固定不变的,所以我们将p当做一个定值去处理。同理j(当前枚举到的位置)也可以认为是一个定值,所以推到这一步我们就可以明白式子拆开的优点:我们可以单独计算和存储这两个定值。那么,假设该怪物在其位置上的有效子弹(包括其他位置溅射过来的)为plus,我们就可以得到这样一个式子:

  • 总伤害(allhurt)=Σ(p-(i-j)^2)

我们将它展开来看,就有:

  • 原式=Σp-Σi^2+Σ2ij-Σj^2

注:p是一个定值,j同理(因为在当前枚举回合内两个值是不变的,所以可以这样认为)。所以我们可以将Σp和Σj提出,且容易证得Σp=plus*p。

而因为i是一个变值,所以i前的Σ就需要通过循环操作来实现。

那么如何来实现呢?

我们可以通过当前枚举到的位置j的后一位(即j+1)得到当前位置的状态。我们通过累加j+1放置的子弹个数来求得plus。但是值得注意的是,如果子弹无法溅射到当前位置,也就是说累加到的子弹数里包含了无效子弹,那么如果不除去无效子弹的个数,就会对我们的计算结果造成影响。那么如何除去呢?

我们再来观察一下溅射伤害计算的公式:溅射伤害=p-(i-j)^2。容易得出,我们的溅射最大范围其实就是sqrt(p)+1。可以手动计算验证一下。我们将这个溅射范围定义为over,即:

  • 溅射范围(over)=sqrt(p)+1

也就是说,如果某个位置放置的子弹已经无法对当前位置的怪物造成伤害,那么我们就可以把该位置上的子弹数从plus中减去。这样我们就实现了计算有效子弹个数的想法。

完成了公式内所有未知数的求值工作,接下来我们就该维护num的值了。当我们求出总伤害(allhurt)之后,就可以计算在当前位置放置的子弹个数了。这里我们进行分类讨论:

如果当前造成的总伤害已经足够让这个位置上的怪物死亡,那么我们不需要更新num值。

如果当前造成的总伤害不能让这个位置上的怪物死亡或者恰好让该怪物的Hp值为0(为0是不代表死亡的,还活着~),那么我们需要在该位置上放置子弹。并且显然,需要放置的子弹个数为:

  • Num[i]=(Hp[i]-allhurt)/p+1

为什么加1呢?因为当Hp恰好等于0的时候怪物还没有死呀qwq~

同时我们定义一个计算总子弹放置数的变量cnt,每次更新完num值后将每个num值累加起来。

因为我们要求的是用k发子弹消灭所有怪物的最小威力值p,所以如果在当前回合的枚举中我们求得的cnt大于要求子弹个数k,我们便对p的范围向左二分,反之向右。

这样我们的判断函数就写好了。

主函数没有什么特别需要强调的,需要的只是输入和简单的二分操作,这里就不再详述。

最后一定要注意的是,每次判断时要对存储计算结果的变量进行清零。(转自

式子

我们设对于二分出的p,射到从左到右第s个怪物身上的子弹数为kk[s]。对于p-(i-j)*(i-j)我们将它拆开,变成-j^2+2ij-i^2+p。对于第j个怪,它受到的溅射伤害应为-(kk[j+1]+...+kk[n])*j^2+2*(kk[j+1]*(j+1)+...+kk[n]*n)*j-(kk[j+1]*(j+1)^2+...+kk[n]*n^2)+(kk[j+1]+...+kk[n])*p,相信看了这个大家就能理解了。

 

#include
#include
#include
#include
using namespace std;typedef long long ll;ll a[501000],k,num[540010],n;bool check(ll mid){ ll t=(ll)sqrt(mid)+1; ll sum=0; memset(num,0,sizeof(num)); ll z=0,i=0,i1=0; for(int j=n;j>=1;j--) { if(num[j+1]) { z+=num[j+1]; i+=(num[j+1]*(j+1)); i1+=(num[j+1]*(j+1)*(j+1)); } if(j+t<=n&&(num[j+t])) { z-=num[j+t]; i-=(num[j+t]*(j+t)); i1-=(num[j+t]*(j+t)*(j+t)); } ll all=1ll*mid*z-1ll*j*j*z+2ll*i*j-i1; if(a[j]>=all) { num[j]=(a[j]-all)/mid+1; sum+=num[j]; } if(sum>k) return 0; } return 1;}int main(){ cin>>n>>k; for(int i=1;i<=n;i++) scanf("%lld",&a[i]); ll l=0,r=1e16; while(l<=r) { ll mid=(l+r)/2; if(check(mid)) r=mid-1; else l=mid+1; } cout<

 

 

 

转载于:https://www.cnblogs.com/kzj-pwq/p/8588260.html

你可能感兴趣的文章
while 循环、格式化输出、运算符
查看>>
Combination Sum III -- leetcode
查看>>
中国剩余定理
查看>>
MongoDB一些基本的命令
查看>>
尚未为数据源“RptDataSet_StatEC”提供数据源实例
查看>>
POJ 1410 Intersection
查看>>
Linux服务部署:nginx服务 nfs服务
查看>>
Spring Boot热部署(springloader)
查看>>
我要写一篇动态计算tableView-cell高度的随笔
查看>>
2.2 数据库高速缓冲区
查看>>
0课1-2节——刚接触开发板之接口接线工具
查看>>
分治法求解最大子段和问题
查看>>
H5实现formdata+ajax+上传进度上传文件
查看>>
iOS 6 编程 - 自动布局(Auto Layout)系列文章
查看>>
一. python的collections模块
查看>>
Linux之路(原发表于07年,现在搬到博客)
查看>>
Varnish
查看>>
20155338 《JAVA程序设计》实验五网络编程与安全实验报告
查看>>
查看Weblogic JNDI 树的几种方式
查看>>
组件之间的通信(持续补充)
查看>>