首页 > 代码库 > BZOJ3203 SDOI2013 保护出题人 凸包+三分法

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 doubleconst 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

 

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