BZOJ3203 保护出题人(defend)

保护出题人(defend)

题目描述

 

 

输入

 

第一行两个空格隔开的正整数n和d,分别表示关数和相邻僵尸间的距离。
接下来n行每行两个空格隔开的正整数,第i + 1行为 a i和 x i,分别表示相比上一关
在僵尸队列排头增加血量为 a i点的僵尸,排头僵尸从距离房子 x i米处开始接近。

 

输出

 

一个数,n关植物攻击力的最小总和 ,保留到整数。

 

样例输入

5 2
3 3
1 1
10 8
4 8
2 3

样例输出

7

提示

 

 

来源

sdoi2013R2day2


solution

把ai前缀和起来

得到一个式子:yi=max{(a[i]-a[j-1])/(x[i]+(i-j)*d)}

拆开 yi=max{(a[i]-a[j-1])/(x[i]+i*d-j*d)}

很像y=(yi-yj)/(xi-xj)

显然凸包上的点有用,那就把( aj,-j*d ) 建凸包。

对于点(a[i],i*d+x[i]) 斜率是单峰的。

那么可以三分。

注意整数三分

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 1000005
#define ll long long
using namespace std;
ll n,d,top;
double ans;
struct node{
    ll a,x;
}s[maxn];
struct po{
    ll x,y;
}zh[maxn],nex,q;
po xl(po a,po b){
    po t;
    t.x=a.x-b.x;t.y=a.y-b.y;
    return t;
}
ll cj(po a,po b){
    return a.x*b.y-a.y*b.x;
}
double getk(po a,po b){
    double dx=a.x-b.x,dy=a.y-b.y;
    return dy/dx;
}
double ask(po q){
    ll l=1,r=top;
    while(r-l>=3){//zhengshu sanfen
        int lm=l+(r-l)/3,rm=r-(r-l)/3;
        double k1=getk(zh[lm],q),k2=getk(zh[rm],q);
        if(k1>k2)r=rm;
        else l=lm;
    }
    double fs=0;
    for(ll i=l;i<=r;i++)
        fs=max(fs,getk(zh[i],q));
    return fs;
}
int main()
{
    cin>>n>>d;
    for(ll i=1;i<=n;i++){
        scanf("%lld%lld",&s[i].a,&s[i].x);
        s[i].a+=s[i-1].a;
    }
    for(ll i=1;i<=n;i++){
        nex.x=d*i;nex.y=s[i-1].a;
        while(top>1&&cj(xl(zh[top],zh[top-1]),xl(nex,zh[top]))<0)top--;
        zh[++top]=nex;
        q.x=s[i].x+d*i;q.y=s[i].a;
        ans+=ask(q);
    }
    printf("%.0lf\n",ans);
    return 0;
}

 

posted @ 2018-09-11 19:56  liankewei123456  阅读(170)  评论(0编辑  收藏  举报