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(ij)sum[i]sum[j1])
然后考虑这个式子,发现可以理解为 ( 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]+di,sum[i])(dj,sum[j1])这两个点的斜率
于是乎可以对后面那个东东做建一个凸包,然后在上面三分(取相邻两个点就是二分)
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;
}
posted @ 2021-06-28 20:09  lahlah  阅读(19)  评论(0编辑  收藏  举报