非常好的题目
山东二轮前几年还是不错的嘛
题解:http://www.cnblogs.com/iwtwiioi/p/4007263.html
前i个僵尸的血量和为sum[i]
那么第i关的攻击力就是
max{(sum[i]-sum[j-1])/(x[i]+i*d-j*d)}(1<=j<=i)
考虑一下为什么?
对于第i只僵尸,可以把前面i-1只僵尸的血量合到第i只上,取最大值已经保证了前i-1只僵尸符合条件
然后就是这个问题怎么快速求解
这个问题可以看做两个点之间的斜率
即所有(j*d,sum[j-1])与(x[i]+i*d,sum[i])之间斜率的最大值
显然这些能取到的点应该在下凸壳上
下凸壳上的点到这个询问的点的斜率是个单峰函数,所以可以三分
于是维护一个下凸壳,然后每次询问在下凸壳上三分即可
山东二轮前几年还是不错的嘛
题解:http://www.cnblogs.com/iwtwiioi/p/4007263.html
前i个僵尸的血量和为sum[i]
那么第i关的攻击力就是
max{(sum[i]-sum[j-1])/(x[i]+i*d-j*d)}(1<=j<=i)
考虑一下为什么?
对于第i只僵尸,可以把前面i-1只僵尸的血量合到第i只上,取最大值已经保证了前i-1只僵尸符合条件
然后就是这个问题怎么快速求解
这个问题可以看做两个点之间的斜率
即所有(j*d,sum[j-1])与(x[i]+i*d,sum[i])之间斜率的最大值
显然这些能取到的点应该在下凸壳上
下凸壳上的点到这个询问的点的斜率是个单峰函数,所以可以三分
于是维护一个下凸壳,然后每次询问在下凸壳上三分即可
注意整数三分,需要判断到r-l<3时,对这几个数单独求解取最大
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 100100
using namespace std;
struct yts
{
double x,y;
}s[maxn];
double sum,d;
double ans;
int n,top;
yts operator-(yts x,yts y)
{
yts ans;
ans.x=x.x-y.x;ans.y=x.y-y.y;
return ans;
}
double operator*(yts x,yts y)
{
return x.x*y.y-x.y*y.x;
}
void insert(yts x)
{
while (top>1 && (x-s[top])*(s[top]-s[top-1])>=0) top--;
s[++top]=x;
}
double cal(yts a,yts b)
{
return (b.y-a.y)/(b.x-a.x);
}
double max(double x,double y) {return x>y?x:y;}
double calc(yts x)
{
int l=1,r=top;
while (r-l>=3)
{
int mid=l+(r-l)/3,midmid=r-(r-l)/3;
if (cal(s[mid],x)>cal(s[midmid],x)) r=midmid; else l=mid;
}
double ans=0.0;
for (int i=l;i<=r;i++) ans=max(ans,cal(s[i],x));
return ans;
}
int main()
{
scanf("%d%lf",&n,&d);
top=0;sum=0;ans=0;
for (int i=1;i<=n;i++)
{
double x,y;
scanf("%lf%lf",&x,&y);
yts t;
t.x=(double)i*d;t.y=sum;
insert(t);
sum+=x;
t.x=y+(double)i*d;t.y=sum;
ans+=calc(t);
}
printf("%.0lf\n",ans);
return 0;
}