luogu P3299 [SDOI2013]保护出题人
https://www.luogu.com.cn/problem/P3299
首先肯定是写出式子
第i关的攻击力为
max
(
s
u
m
[
i
]
−
s
u
m
[
j
−
1
]
x
[
i
]
+
d
∗
(
i
−
j
)
)
\large \max(\frac{sum[i]-sum[j-1]}{x[i]+d*(i-j)})
max(x[i]+d∗(i−j)sum[i]−sum[j−1])
然后考虑这个式子,发现可以理解为
(
x
[
i
]
+
d
∗
i
,
s
u
m
[
i
]
)
和
(
d
∗
j
,
s
u
m
[
j
−
1
]
)
(x[i]+d*i,sum[i])和(d*j,sum[j-1])
(x[i]+d∗i,sum[i])和(d∗j,sum[j−1])这两个点的斜率
于是乎可以对后面那个东东做建一个凸包,然后在上面三分(取相邻两个点就是二分)
code:
#include<bits/stdc++.h>
#define N 200050
#define ll long long
#define double long double
using namespace std;
int n, d, a[N], b[N], sta[N];
ll sum[N];
double x(int i) {
return (double) d * i;
}
double y(int i) {
return (double) sum[i - 1];
}
double slope(int i, int j) {
return (y(j) - y(i)) / (x(j) - x(i));
}
double sslope(int i, double xx, double yy) {
return (yy - y(i)) / (xx - x(i));
}
int main() {
scanf("%d%d", &n, &d);
for(int i = 1; i <= n; i ++) scanf("%d%d", &a[i], &b[i]);
for(int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + a[i];
double ans = 0; int top = 0, tot = n + 1;
for(int i = 1; i <= n; i ++) {
while(top && slope(sta[top - 1], sta[top]) > slope(sta[top], i)) top --;
sta[++ top] = i;
int l = 0, r = top + 1;
while(l + 1 < r) {
int mid = (l + r) >> 1;
if(sslope(sta[mid], b[i] + 1ll * d * i, sum[i]) > sslope(sta[mid - 1], b[i] + 1ll * d * i, sum[i])) l = mid;
else r = mid;
}
// printf("%d %Lf (%Lf %Lf) (%Lf %Lf)\n", l, slope(sta[l], i), x(sta[l]), y(sta[l]), x(i), y(i));
ans += sslope(sta[l], b[i] + 1ll * d * i, sum[i]);
}
printf("%.0Lf", ans);
return 0;
}