[BZOJ3203][SDOI2013]保护出题人

bzoj
luogu
题面不太好简化就不放了qaq。

sol

先对僵尸的血量做一个前缀和,然后在第(i)关中视第(j)只僵尸((jle i))的血量为(a_i-a_{j-1}),这样就可以当作是开了穿墙挂,可以一直攻击每一只僵尸直至其死亡。
考虑最优策略,一定是某一只僵尸在刚好走到门前的时候把他打死。也就是算出打死每一只僵尸所需的伤害,放在一起求个(max)
形式化的,有(y_i=max_{1le jle i}{frac{a_i-a_{j-1}}{x_i+d imes(i-j)}})。感觉这个东西长得很像是个斜率。
所以(y_i)就是点((x_i+di,a_i))到所有((dj,a_{j-1}))的斜率最大值。而斜率最大值一定会出现在下凸壳上(别问我怎么知道的),所以对所有的((di,a_{i-1}))维护个下凸壳,然后每次查询的时候二分即可。
复杂度(O(nlog n))

code

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll gi(){
	ll x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 1e5+5;
int n,top;ll d,a[N],x[N];double ans;
struct node{ll x,y;}q[N];
double slope(node p,node q){
	return 1.0*(p.y-q.y)/(p.x-q.x);
}
int main(){
	n=gi();d=gi();
	for (int i=1;i<=n;++i) a[i]=a[i-1]+gi(),x[i]=gi();
	for (int i=1;i<=n;++i){
		node tmp=(node){d*i,a[i-1]};
		while (top&&slope(q[top-1],q[top])>slope(q[top],tmp)) --top;
		q[++top]=tmp;tmp=(node){x[i]+d*i,a[i]};
		int l=1,r=top,res=0;
		while (l<=r){
			int mid=l+r>>1;
			if (slope(q[mid-1],tmp)<slope(q[mid],tmp)) res=mid,l=mid+1;
			else r=mid-1;
		}
		ans+=slope(q[res],tmp);
	}
	printf("%.0lf
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/zhoushuyu/p/9267467.html