[BZOJ3203][SDOI2013]保护出题人
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;
}