火枪打怪
题目链接:https://www.luogu.com.cn/problem/P1485
题解:
二分枚举p,判断能否满足题目要求即可。
check:因为每颗子弹只会向左溅射,所以从右向左考虑每个怪物需要多少次攻击。在考虑当前状态时,需要减去怪物被子弹溅射所受的伤害。当第
i
i
i只怪物在攻击
j
j
j时溅射范围内时,其受到的伤害为
p
−
(
i
−
j
)
2
=
p
−
i
2
−
j
2
+
2
∗
i
∗
j
p-(i-j)^2 = p-i^2-j^2+2*i*j
p−(i−j)2=p−i2−j2+2∗i∗j。其中
p
,
i
p,i
p,i已知,统计攻击次数,
j
2
,
2
∗
j
j^2,2*j
j2,2∗j部分的和即可(可以利用差分)。
注意怪物血量需少于0。
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<set>
#include<vector>
#include<queue>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 500100;
const int mod = 998244353;
int b[maxn], e[maxn];
LL a[maxn], c[maxn];
bool check(LL mid, int n, int k);
int main()
{
int n, m, i, j, k;
LL l, r, mid;
scanf("%d %d", &n, &k);
for(i=1;i<=n;i++){
scanf("%lld", &a[i]);
a[i]++;
}
l = 0, r = 1e11;
while(l<r)
{
mid = (l+r)/2;
if(check(mid, n, k))r = mid;
else l = mid+1;
}
printf("%lld\n", l);
return 0;
}
bool check(LL mid, int n, int k)
{
int i, j;
LL num = 0, l = sqrt(mid)+1e-8, bb, xx, yy;
bb = xx = yy = 0;
for(i=n;i>=1;--i){
if(i+l+1<=n){
int pos = i+l+1;
bb -= c[pos];
xx -= 1LL*pos*pos*c[pos];
yy -= 2*pos*c[pos];
}
LL s = max(0LL, a[i]-(mid*bb-bb*i*i-xx+yy*i));
if(s%mid == 0)c[i] = s/mid;
else c[i] = s/mid+1;
num += c[i];
if(c[i]){
bb += c[i];
xx += 1LL*i*i*c[i];
yy += 2LL*i*c[i];
}
}
return num <= k;
}
本文详细解析了洛谷P1485火枪打怪问题的解决方案,采用二分枚举算法结合差分技巧,有效地判断在特定参数下是否能满足题目要求的攻击次数限制。

338

被折叠的 条评论
为什么被折叠?



