[BZOJ3203][SDOI2013]保护出题人

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

sol

先对僵尸的血量做一个前缀和,然后在第\(i\)关中视第\(j\)只僵尸\((j\le i)\)的血量为\(a_i-a_{j-1}\),这样就可以当作是开了穿墙挂,可以一直攻击每一只僵尸直至其死亡。
考虑最优策略,一定是某一只僵尸在刚好走到门前的时候把他打死。也就是算出打死每一只僵尸所需的伤害,放在一起求个\(\max\)
形式化的,有\(y_i=\max_{1\le j\le i}\{\frac{a_i-a_{j-1}}{x_i+d\times(i-j)}\}\)。感觉这个东西长得很像是个斜率。
所以\(y_i\)就是点\((x_i+di,a_i)\)到所有\((dj,a_{j-1})\)的斜率最大值。而斜率最大值一定会出现在下凸壳上(别问我怎么知道的),所以对所有的\((di,a_{i-1})\)维护个下凸壳,然后每次查询的时候二分即可。
复杂度\(O(n\log 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\n",ans);
	return 0;
}
posted @ 2018-07-05 11:35  租酥雨  阅读(215)  评论(0编辑  收藏  举报