洛谷 1485 火枪打怪
【题解】
二分答案+差分
本题要求出尽量小的威力值P,很容易想到二分答案
而本题难点在于二分答案之后的检验。每发子弹对一个区间造成影响,我们可以尝试使用差分。但每发子弹对区间不同位置的影响是不同的。因此我们可以用多阶差分。
a3 | 0 | -p | -p+1 | -p+4 | -p+9 | ... | -p+s2 | 0 | 0 | 0 | 0 |
a2 | 0 | 0 | 1 | 3 | 5 | ... | 2s-1 | p-s2 | 0 | 0 | 0 |
a1 | 0 | 0 | 1 | 2 | 2 | ... | 2 | p-(s+1)2+2 | -p+s2 | 0 | 0 |
a0 | 0 | 0 | 1 | 1 | 0 | ... | 0 | p-(s+1)2 | -2p+2s2+2s-1 |
p-s2 | 0 |
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #define LL long long 5 using namespace std; 6 const int maxn=1000010; 7 LL n,k,l,r,mid,ans,a0[maxn],a1[maxn],a2[maxn],a3[maxn],b[maxn],b2[maxn]; 8 inline LL read(){ 9 LL k=0,f=1; char c=getchar(); 10 while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar(); 11 while('0'<=c&&c<='9')k=k*10+c-'0',c=getchar(); 12 return k*f; 13 } 14 inline LL max(LL x,LL y){return x>y?x:y;} 15 inline LL min(LL x,LL y){return x<y?x:y;} 16 bool check(LL x){ 17 for(int i=0;i<=n+1;i++) a0[i]=a1[i]=a2[i]=a3[i]=0,b[i]=b2[i];//初始化 18 LL num=0,stop=sqrt(x),cnt=0; 19 for (long long i=1;i<=n;i++) { 20 a1[i]+=a1[i-1]+a0[i]; 21 a2[i]+=a2[i-1]+a1[i]; 22 a3[i]+=a3[i-1]+a2[i]; 23 b[i]+=a3[i]; 24 num=(b[i]/x)+ (b[i]%x==0?0:1);//num表示打死i号怪物需要多少枪 25 if (b[i]<=0) continue; 26 if ((cnt+=num)>k) return 0; 27 a3[i]-=num*x; 28 a0[i+1]+=num; a0[i+2]+=num; 29 a0[min(i+stop,n)+1]+=(x-(stop+1)*(stop+1))*num; 30 a0[min(i+stop,n)+2]+=(-2*x+stop*stop+(stop+1)*(stop+1)-2)*num; 31 a0[min(i+stop,n)+3]-=(stop*stop-x)*num; 32 } 33 return cnt<=k; 34 } 35 int main(){ 36 n=read(); k=read(); 37 for(int i=n;i;i--) b[i]=b2[i]=read()+1,r=max(r,b[i]+1LL*(i-1)*(i-1));//先把数组倒过来,方面后面的差分 38 l=0; r<<=1; 39 while(l+1<r) if(check(mid=(l+r)>>1)) r=mid; else l=mid; 40 return printf("%lld\n",r),0; 41 }