您的位置:首页 > 其它

【bzoj3203】[Sdoi2013]保护出题人 凸包+三分法

2016-03-14 15:01 351 查看
非常好的题目

山东二轮前几年还是不错的嘛

题解: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: