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;
}