bzoj 3203: [Sdoi2013]保护出题人 凸包

题目大意:

http://www.lydsy.com/JudgeOnline/problem.php?id=3203

题解

首先我们考虑对一大波僵尸来袭的情况进行分析
假设来袭的僵尸是\(\{ a_1,a_2,a_3,...,a_n\}\)
那么距离分别为\(\{dis,dis+d,dis+d*2,...,dis+d*(n-1)\}\)
那么我们知道,在这波僵尸中,我们应该取到的最小的攻击速度为
\(max{\frac{sum_i}{dis_i}}\)
其中\(sum_n = \sum_{i=1}^{n}a_i,dis_i = dis + (i-1)*d\)
然后我们把这个式子转化到原序列中,发现式子变成了下面这个样子
假设排头的僵尸是\(p\) :
那么有\(ans_p = max_{i=1}^n\{\frac{sum_p - sum_{i-1}}{(dis_p+p*d - i*d)}\}\)
而我们的目的就是最小化\(\sum_{i=1}^nans_i\)
我们观察上面关于\(ans_p\)的式子,发现这实际上是两个点的斜率
\((dis_p+p*d,sum_p)\)\((i*d,sum_{i-1})\)两个点的斜率
所以我们枚举每个p,然后对于每个p都计算出所有的\((i*d,sum_{i-1})\)
然后再取斜率最大值即可...
TLE
我们发现每次针对不同的p计算的时候\((i*d,sum_{i-1})\)居然都是相同的!
所以我们根本不需要每次都重新计算
那么每次当我们枚举到一个p的时候,计算所有满足\(i\leq p\)的点中与其最大斜率即可
那怎么计算这个最大斜率呢??
我们知道每次加入的点在横纵坐标上一定都比上一次加入的点要大
说起来比较矛盾,但是我们需要维护一个不会将任何点包括的"上凸壳"
然后在这个"上凸壳"上的斜率明显是一个单峰函数,所以我们三分即可.

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(ll &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
const int maxn = 100010;
const double eps = 1e-9;
inline int dcmp(const double &x){
    if(x < eps && x > -eps) return 0;
    return x > 0 ? 1 : -1;
}
struct Point{
    double x,y;
    Point(const double &a = 0,const double &b = 0){x=a;y=b;}
    void print(){
        printf("Point : (%lf,%lf)\n",x,y);
    }
};
typedef Point Vector;
inline Vector operator + (const Vector &a,const Vector &b){
    return Vector(a.x+b.x,a.y+b.y);
}
inline Vector operator - (const Vector &a,const Vector &b){
    return Vector(a.x-b.x,a.y-b.y);
}
inline double cross(const Vector &a,const Vector &b){
    return a.x*b.y - a.y*b.x;
}
inline double slope(const Point &a,const Point &b){
    return (a.y - b.y)/(a.x - b.x);
}
Point p[maxn];int m = 0;
inline void insert(const Point &x){
    while(m > 1 && dcmp(cross(p[m] - p[m-1],x - p[m])) <= 0) -- m;
    p[++m] = x;
}
ll a[maxn],dis[maxn],sum[maxn];
double solve(const Point &x){
    int l = 1,r = m,midx,midy;
    while(l <= r-3){
        midx = (l+l+r)/3;midy = (l+r+r)/3;
        if(slope(p[midx],x) > slope(p[midy],x)) r = midy;
        else l = midx;
    }
    double ans = .0;
    for(int i=l;i<=r;++i) ans = max(ans,slope(p[i],x));
    return ans;
}
int main(){
    ll n,d;read(n);read(d);
    for(int i=1;i<=n;++i){
        read(a[i]);read(dis[i]);
        sum[i] = sum[i-1] + a[i];
    }double ans = .0;
    for(int i=1;i<=n;++i){
        insert(Point(i*d,sum[i-1]));
        ans += solve(Point(dis[i]+i*d,sum[i]));
    }printf("%.0f\n",ans);
    getchar();getchar();
    return 0;
}
posted @ 2017-02-23 21:25  Sky_miner  阅读(311)  评论(0编辑  收藏  举报