BZOJ3203 SDOI2013 保护出题人 凸包+三分法

题意:给定N组询问和D,初始时集合为空,每组询问先向集合的开头插入一个元素xi,然后给出一个数pi,求最小的yi使得\[{y}_{i}\left({p}_{i}+D\left(i-1 \right) \right)\geq {x}_{i}\]

题解:设Si=$\sum\limits_{j = 1}^i{x}_{j}$,显然yi=$max\left \{ \frac{{S}_{i}-{S}_{j-1}}{x_{i}+D\left ( i-j \right )} \right \},1\leq j\leq i$,设$a_j=D\cdot j,b_j=S_{j-1}$,那么我们就是要求(ak,bk)使得其与$\left ( x_i+D\cdot i,S_i \right )$的连线斜率最小,显然这个点一定是在一个由(a,b)组成的凸包上,而且斜率的取值一定是一个单峰,三分即可

#include <cstdio>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define eps 1e-7
#define ll long long
#define ld long double

const int MAXN=100000+2;
struct Point{
    ld x,y;
    Point(){}
    Point(ld _x,ld _y):x(_x),y(_y){}
}Sta[MAXN];
int N,Top;
ll D,S[MAXN],x;
ld Ans;

ld Calc(Point a,Point b){ return (b.y-a.y)/(b.x-a.x);}

void Insert(Point x){
    while(Top>=2 && Calc(Sta[Top-1],Sta[Top])>=Calc(Sta[Top],x)-eps) Top--;
    Sta[++Top]=x;
}

ld Find(Point x){
    ld lk,rk,Ret=-1;
    int l=1,r=Top,lm,rm;
    while(r-l>=3){
        lm=(l+l+r)/3,rm=(l+r+r)/3;
        lk=Calc(Sta[lm],x),rk=Calc(Sta[rm],x);
        if(lk>rk) r=rm;
        else l=lm;
    }

    for(int i=l;i<=r;i++) Ret=max(Ret,Calc(Sta[i],x));
    return Ret;
}

int main(){
    cin >> N >> D;
    for(int i=1;i<=N;i++){
        scanf("%lld %lld",S+i,&x),S[i]+=S[i-1];
        Insert(Point(i*D,S[i-1])),Ans+=Find(Point(i*D+x,S[i]));
    }
    cout << fixed << setprecision(0) << Ans << endl;

    return 0;
}
View Code

 

posted @ 2017-04-02 17:21  WDZRMPCBIT  阅读(175)  评论(0编辑  收藏  举报