Description
Input
第一行两个空格隔开的正整数n和d,分别表示关数和相邻僵尸间的距离。接下来n行每行两个空格隔开的正整数,第i + 1行为Ai和
Xi,分别表示相比上一关在僵尸队列排头增加血量为Ai 点的僵尸,排头僵尸从距离房子Xi米处开始接近。
Output
一个数,n关植物攻击力的最小总和 ,保留到整数。
Sample Input
5 2
3 3
1 1
10 8
4 8
2 3
Sample Output
7
HINT
第一关:距离房子3米处有一只血量3点的僵尸,植物最小攻击力为1.00000;
第二关:距离房子1米处有一只血量1点的僵尸、3米处有血量3点的僵尸,植物最小攻击力为1.33333;
第三关:距离房子8米处有一只血量10点的僵尸、10米处有血量1点的僵尸、12米处有血量3点的僵尸,植物最小攻击力为1.25000;
第四关:距离房子8米处有一只血量4点的僵尸、10米处有血量10点的僵尸、12米处有血量1点的僵尸、14米处有血量3点的僵尸,植物最小攻击力为1.40000;
第五关:距离房子3米处有一只血量2点的僵尸、5米处有血量4点的僵尸、7米处有
血量10点的僵尸、9米处有血量1点的僵尸、11米处有血量3点的僵尸,植物最小攻击力 为2.28571。
植物攻击力的最小总和为7.26905。对于100%的数据, 1≤n≤10^5,1≤d≤10^12,1≤x≤ 10^12,1≤a≤10^12
题解
这题很强
一开始以为是道水题2333…然后就挂了
可以搞出来一个式子
ans=max((s[i]−s[j−1])/(Xi+d∗(i−j)))ans=max((s[i]−s[j−1])/(Xi+d∗(i−j)))
其中s表示A的前缀和
意思就是打死每个僵尸所需要的攻击力嘛
我们设两个点Q(x1,y1),P(x2,y2)Q(x1,y1),P(x2,y2)
其中y1=s[i],x1=Xi+d∗iy1=s[i],x1=Xi+d∗i
y2=s[j−1],x2=d∗jy2=s[j−1],x2=d∗j
然后就可以化简成
ans=max((y2−y1)/(x2−x1))ans=max((y2−y1)/(x2−x1))
这是一个斜率的形式,那么问题转化为给一个点和其他一些点,求这个点到其他一些点的最大斜率
N^2的做法肯定是不兹磁的
画画图可以发现,这些点一定在一个下凸包上
于是每次输入可以用单调栈维护下凸包
发现答案满足三分性,三分答案即可
输入不能输longlong然后强制转double????
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
struct pt{double x,y;}sta[110000];
int n,D,top;
double slop(pt u,pt v){return (v.y-u.y)/(v.x-u.x);}
double s[110000];
double ans;
int main()
{
scanf("%d%d",&n,&D);
top=0;
for(int T=1;T<=n;T++)
{
double A,X;
scanf("%lf%lf",&A,&X);
s[T]=s[T-1]+A;
pt tmp;tmp.x=D*double(T);tmp.y=s[T-1];
while(top && slop(sta[top-1],sta[top])>=slop(sta[top],tmp))top--;
sta[++top]=tmp;
tmp.x=X+D*double(T);tmp.y=s[T];
int l=1,r=top;
while(r-l>3)
{
int mid=(l+r)/2;
int mmid=(mid+r)/2;
if(slop(tmp,sta[mid])>slop(tmp,sta[mmid]))r=mmid-1;
else l=mid+1;
}
int op=l;
for(int i=l+1;i<=r;i++)
if(slop(tmp,sta[i])>slop(tmp,sta[op]))op=i;
ans+=slop(tmp,sta[op]);
}
printf("%.0lf\n",ans);
return 0;
}