先考虑暴力怎么做。
记
Si
S
i
表示前
i
i
个僵尸(按照输入顺序)的血量之和。
考虑如何判断在第关,植物攻击力为
mid
m
i
d
是否可行。
容易得出,消灭前
i
i
个僵尸(排头兵为第个)需要
Si−Sj−1mid
S
i
−
S
j
−
1
m
i
d
的时间,只要保证在这段时间内僵尸不进房子即可。
也就是说,植物攻击力为
mid
m
i
d
时,可行的条件是:
对于每一个
1≤j≤i
1
≤
j
≤
i
,都有:
所以,第 i i 关植物的最小攻击力为:
这样是 O(n2) O ( n 2 ) 的。但可以发现上式是一个斜率方程,
表示 (d×j,Sj−1) ( d × j , S j − 1 ) 和 (Xi+d×i,Si) ( X i + d × i , S i ) 之间的斜率。
因此将第 i i 个僵尸看作一个点,维护前 i i 个点构成的下凸壳。
可以看出,最大值的点一定在下凸壳上。
又由于这一点到下凸壳上点的斜率是单峰的,因此可以三分查找最大值。
(其实就是求下凸壳过点 (Xi+d×i,Si) ( X i + d × i , S i ) 的切线斜率)
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n, top, stk[N];
ll d, sum[N], X[N], X_[N], Y_[N];
double ans;
double slope(ll X1, ll Y1, ll X2, ll Y2)
{
return 1.0 * (Y2 - Y1) / (X2 - X1);
}
bool check(int p1, int p2, int p3)
{
return slope(X_[p1], Y_[p1], X_[p2], Y_[p2]) >
slope(X_[p2], Y_[p2], X_[p3], Y_[p3]);
}
int main()
{
int i;
scanf("%d%lld", &n, &d);
For (i, 1, n)
{
scanf("%lld%lld", &sum[i], &X[i]);
sum[i] += sum[i - 1];
X_[i] = d * i; Y_[i] = sum[i - 1];
while (top > 1 && check(stk[top - 1], stk[top], i))
top--;
stk[++top] = i;
ll x = X[i] + d * i, y = sum[i];
int l = 1, r = top;
while (l != r)
{
int cm = (r - l + 1) / 3;
int mid1 = l + cm - 1, mid2 = l + (cm << 1) - 1;
if (mid1 == mid2) break;
if (slope(x, y, X_[stk[mid1]], Y_[stk[mid1]]) <
slope(x, y, X_[stk[mid2]], Y_[stk[mid2]]))
l = mid1 + 1;
else r = mid2 - 1;
}
ans += max(slope(x, y, X_[stk[l]], Y_[stk[l]]),
slope(x, y, X_[stk[r]], Y_[stk[r]]));
}
printf("%.0lf\n", ans);
return 0;
}