洛谷 1485 火枪打怪

Description

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就可以消灭所有怪物。

solution

**正解:二分答案
朴素做法从后往前扫,因为右边的点不能依赖左边的点的溅射伤害,所以如果还剩下血量必须得打掉,然后计算对左边的溅射伤害,计算溅射伤害是 \(\sqrt(n)\) 的,考虑优化这个地方,伤害的式子是 $$p-(i-j)^2$$

\[p-(i^2-2*i*j+j^2) \]

拆开之后非常好维护,这是一个 移动一个\(sqrt(n)\) 的窗口的过程,我们只需要维护窗口中的数的数量和 \(j^2\)\(j\) 的和即可,可以 \(O(1)\) 求出当前剩余的血量**

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#include<queue>
#define RG register
#define il inline
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=500005;
int n,K;ll a[N],c[N];
il bool check(ll mid){
    ll tmp,ret=0,xy;
    RG int i;
   memset(c,0,sizeof(c));
   int block=sqrt(mid)+1;
   ll xif=0,cnt=0,xi=0;
   for(i=n;i>=1;i--){
      if(i+block<=n && c[i+block]>0){
         tmp=i+block;
         xif-=c[tmp]*tmp*tmp;
         cnt-=c[tmp];
         xi-=c[tmp]*tmp;
      }
      tmp=mid*cnt-cnt*i*i+2*xi*i-xif;
      tmp=a[i]-tmp;
      if(tmp>0){
         xy=(tmp+mid-1)/mid;
         xif+=xy*i*i;
         xi+=xy*i;
         cnt+=xy;
         ret+=xy;
         c[i]=xy;
      }
      if(ret>K)return 0;
   }
    return ret<=K;
}

void work()
{
    ll l=1,r=2e14,mid,ans=0;
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]),a[i]++;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%lld\n",ans);
}

int main()
{
  work();
  return 0;
}

posted @ 2017-11-01 18:46  Hxymmm  阅读(222)  评论(0编辑  收藏  举报