本题二分显然
关键是怎么O(n)check
我们很容易想到应该从右往左打起
我们设对于二分出的伤害值为 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。
这样,我们就可以从右往左递推,第 j 个怪物所受到的溅射伤害可通过其前面所有怪物伤害累加系数得到
如果 p - ( i - j ) * ( i - j ) < 0 时将对应系数减去即可
由此O(n log n)算法就可以过了
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 using namespace std; 5 #define ll long long 6 #define N 500010 7 ll n,k,a[N],use[N],lim; 8 ll read(){ 9 ll sum=0; 10 char ch=getchar(); 11 while (ch<'0'||ch>'9') ch=getchar(); 12 while (ch>='0'&&ch<='9'){ 13 sum=sum*10+ch-'0'; 14 ch=getchar(); 15 } 16 return sum; 17 } 18 int check(ll x){ 19 ll t1,t2,t3,used,life; 20 memset(use,0,sizeof(use)); 21 t1=t2=t3=used=0; 22 for (int j=n;j>=1;j--){ 23 if (use[j+1]){ 24 t1+=use[j+1]; 25 t2+=use[j+1]*(j+1); 26 t3+=use[j+1]*(j+1)*(j+1); 27 } 28 if (j+lim<=n&&use[j+lim]){ 29 t1-=use[j+lim]; 30 t2-=use[j+lim]*(j+lim); 31 t3-=use[j+lim]*(j+lim)*(j+lim); 32 } 33 life=2*t2*j-t1*j*j-t3+t1*x; 34 if (a[j]>=life){ 35 use[j]=(a[j]-life)/x+1; 36 used+=use[j]; 37 } 38 if (used>k) return 0; 39 } 40 return 1; 41 } 42 int main(){ 43 ll mid,l,r=0,ans; 44 n=read(); 45 k=read(); 46 for (int i=1;i<=n;i++){ 47 a[i]=read(); 48 r+=a[i]; 49 } 50 l=a[n]/k+1;//下界 51 r=r/k+1;//上界 52 while (l<r){ 53 mid=(l+r)>>1; 54 lim=(ll)sqrt(mid)+1; 55 if (check(mid)) r=mid; 56 else l=mid+1; 57 } 58 printf("%lld",l); 59 return 0; 60 }