[BZOJ3203]-[Sdoi2013]保护出题人-凸包+三分

来源:互联网 发布:淘宝主图视频制作教程 编辑:程序博客网 时间:2024/06/05 07:29

说在前面

第一次写三分,比想象的要艰辛啊…
因为这道题是在搜索「三分」的时候发现的,于是直接就看到题解了…
不过看题解的时候快要困的睡着了…看半天半懂不懂的….
粗略推导了一下之后就开始写了,于是各种小bug= =

精神状态差的时候果然要拒绝写题!!!


题目

BZOJ3203传送门

题面

懒得打字了=w=,直接粘图岂不美滋滋
这里写图片描述

输入输出格式

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

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


解法

由题意,前面的僵尸死了之后,后面的僵尸才能受到伤害,带着这样的时间限制是很难处理的。为了去掉这个限制,可以把后面僵尸的血量加上前面僵尸的血量总和,这样的处理与原题意等价

这样问题就转化成了:有很多僵尸,它们从一开始就受到持续伤害,并且在到达房子之前死掉,询问这个持续伤害的最小值。很明显答案为:ans[i]=max()=max(sum[i]sum[j1]x[i]+idjd)sum数组表示血量前缀和

但是N规模1e5,直接枚举肯定T的稳稳的
这时候就要靠智商思维了。注意到sum[i]sum[j1]x[i]+idjd与斜率形式很相似,我们把( x[i]+id,sum[i] )作为P点,而(sum[j1],jd)作为Qj点,那么答案就是P点到所有Qj点斜率的最大值。随着j的增加,Qj的横纵坐标都增加,P点也是一样的,因此这个斜率最大值可以用凸包维护三分查找,可以自己在纸上画个图看看

这里写图片描述


下面是自带大常数的代码

这道题需要注意细节…最好想清楚再写

#include <cstdio>#include <cstring>#include <algorithm>using namespace std ;int N , topp ;long long d , A[100005] , X[100005] , sum[100005] ;struct Vector{    double x , y ;    Vector(){} ;    Vector( double x_ , double y_ ):        x(x_) , y(y_){} ;} ;typedef Vector Point ;typedef Vector Vv ;Vv operator - ( const Vv &A , const Vv &B ){ return Vector( A.x - B.x , A.y - B.y ) ;}double Cross( const Vv &A , const Vv &B ){ return A.x * B.y - A.y * B.x ; }Point p[100005] , a[100005] ;double K( int Q , int P ){    return 1.0 * ( sum[P] - p[Q].y ) / ( X[P] + a[P].x - p[Q].x ) ;}double calcu( int now ){    int lf = 1 , rg = topp ;    double rt = 0 ;    while( rg - lf >= 3 ){        int Lmid = lf + ( rg - lf ) / 3 , Rmid = rg - ( rg - lf ) / 3 ;        if( K( Lmid , now ) < K( Rmid , now ) )            lf = Lmid ;        else rg = Rmid ;    }    for( int i = lf ; i <= rg ; i ++ )        rt = max( rt , K( i , now ) ) ;    return rt ;}void solve(){    double ans = 0 ;    topp = 0 ;    for( int i = 1 ; i <= N ; i ++ ){        while( topp >= 2 && Cross( a[i] - p[topp-1] , p[topp] - p[topp-1] ) >= 0 ) topp -- ;        p[++topp] = a[i] ;        ans += calcu( i ) ;    }    printf( "%.0f" , ans ) ;}int main(){    scanf( "%d%lld" , &N , &d ) ;    for( int i = 1 ; i <= N ; i ++ ){        scanf( "%lld%lld" , &A[i] , &X[i] ) ;        sum[i] = sum[i-1] + A[i] ;        a[i] = Point( 1.0 * d * i , 1.0 * sum[i-1] ) ;    }    solve() ;}
原创粉丝点击