您的位置:首页 > 其它

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

2017-12-05 21:34 363 查看

说在前面

第一次写三分,比想象的要艰辛啊…

因为这道题是在搜索「三分」的时候发现的,于是直接就看到题解了…

不过看题解的时候快要困的睡着了…看半天半懂不懂的….

粗略推导了一下之后就开始写了,于是各种小bug= =

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

题目

BZOJ3203传送门

题面

懒得打字了=w=,直接粘图岂不美滋滋



输入输出格式

输入格式:

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

输出格式:

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

解法

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

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

但是N规模1e5,直接枚举肯定T的稳稳的

这时候就要靠智商思维了。注意到sum[i]−sum[j−1]x[i]+i∗d−j∗d与斜率形式很相似,我们把( x[i]+i∗d,sum[i] )作为P点,而(sum[j−1],j∗d)作为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() ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: