zoukankan      html  css  js  c++  java
  • 洛谷 P1485 火枪打怪

    题目描述

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

    SOL

    我们显然可以发现这道题的二分性质。再细心观摩一波数据,50W,只能用O(nlogn)及以下的算法。二分需要一个log,这便要求我们O(n)处理check函数。

    比较显然,我们可以差分。我们发现只有右边对左边有贡献,不妨从右到左做,我们发现这是一个二次多项式,那么可以二阶差分,对于单一的子弹来说,他的

    伤害的差分是一个等差数列,K=2n-1,那对差分数组进行差分,我们统计对当前怪物有贡献的子弹,那么这个伤害的二阶差分就是两倍的子弹数。那么我们还

    有一个问题,那就是子弹的伤害不可能是负数。我们有这样一个方法,我们在一颗子弹的伤害区域的最左侧打标记  -1,标示该位置之后出子弹范围,那么我们同

    时在一阶差分数组中把多余的伤害减掉,那么这颗子弹之后就没有贡献了。

    #include<bits/stdc++.h>
    #define sight(c) ('0'<=c&&c<='9')
    #define LL long long
    inline void read(int &x) {
        static char c;
        for (;!sight(c);c=getchar());
        for (x=0;sight(c);c=getchar())x=x*10+c-48;
    }
    inline void read(long long &x) {
        static char c;static int b;
        for (b=1;!sight(c);c=getchar())if (c=='-') b=-1;
        for (x=0;sight(c);c=getchar())x=x*10+c-48;
        x*=b;
    }
    #define N 501007
    #define min(a,b) (a)<(b)?(a):(b)
    #define HP a
    LL len,ans2,ans,Ans,cm,n,dle,a[N],k,r,c1[N],c2[N],c3[N],c4[N],hp[N];
    bool check(LL P)
    {
        int sss=sqrt(P); int tot=0;
        LL t1=0,t2=0,t3=0,t0=0;
        memset(c1,0,sizeof(c1)); memset(c2,0,sizeof(c2));
        memset(c3,0,sizeof(c3)); memset(c4,0,sizeof(c4));
        for(int i=1;i<=n;++i)hp[n-i+1]=HP[i]+1;//记得+1,因为怪物0血还没有死
        for(int i=1;i<=n;++i)
        {
            t3+=c3[i]; t2+=c2[i];
            t1+=c1[i]+t2; t0+=t1+c4[i];
            hp[i]-=1ll*P*t3-t0;
            if(hp[i]<=0)continue; int gg=((hp[i]+P-1)/P);
            tot+=gg; if(tot>k)return false;
            c3[i+1]+=gg;c3[min(n+1,i+sss+1)]-=gg;
            c2[i+1]+=1ll*2*gg;c2[min(n+1,i+sss+1)]-=2*gg;
            c1[i+1]-=gg;c1[min(n+1,i+sss+1)]-=1ll*(1ll*2*sss-1)*gg;
            c4[min(n+1,i+sss+1)]-=1ll*sss*sss*gg;
        }
        return tot<=k;
    }
    int main () {
        read(n); read(k);
        for (int i=1;i<=n;i++) read(a[i]);
        r=1ll<<53;
        while (r) {
            if (!check(ans+r)) ans+=r;
            r>>=1;
        } 
        printf("%lld
    ",ans+1); return 0;
    }
  • 相关阅读:
    磁盘分区,fdisk,gdisk,开机自动挂载,swap分区,修复文件系统,备份文件
    进程脱离窗口运行,僵尸、孤儿进程
    top命令、kill命令
    进程状态
    rpm包、挂载、yum命令
    DRF源码分析
    forms组件源码
    Django CBV源码分析
    魔法方法
    鸭子类型
  • 原文地址:https://www.cnblogs.com/rrsb/p/8017853.html
Copyright © 2011-2022 走看看