1. 程式人生 > >【bzoj3203】[Sdoi2013]保護出題人 凸包+二分

【bzoj3203】[Sdoi2013]保護出題人 凸包+二分

family 空格 pre height include pos 每次 soft 分享

題目描述

技術分享圖片

輸入

第一行兩個空格隔開的正整數n和d,分別表示關數和相鄰僵屍間的距離。接下來n行每行兩個空格隔開的正整數,第i + 1行為Ai和 Xi,分別表示相比上一關在僵屍隊列排頭增加血量為Ai 點的僵屍,排頭僵屍從距離房子Xi米處開始接近。

輸出

一個數,n關植物攻擊力的最小總和 ,保留到整數。

樣例輸入

5 2
3 3
1 1
10 8
4 8
2 3

樣例輸出

7


題解

凸包+二分

把第 $i$ 只僵屍的血量看作前 $i$ 只僵屍的血量的前綴和,那麽就相當於所有僵屍同時受到傷害。

把僵屍的距離堪稱 $x$ ,血量看成 $y$ ,要求的就是 $\frac yx$ 的最大值。顯然最大值一定在上凸殼上取到,因此維護凸殼,在凸殼上二分即可。

然而每次加入的僵屍位置是隊頭,相當於把所有後面僵屍的血量都加上 $a_i$ 。我們不能實現整體加減,因此需要平移原點。調整距離同理。

時間復雜度 $O(n\log n)$

#include <cstdio>
typedef long double ld;
struct point
{
	ld x , y;
	point() {}
	point(ld a , ld b) {x = a , y = b;}
}sta[100010] , O(0 , 0) , P;
int top;
inline ld slop(point a , point b)
{
	return (b.y - a.y) / (b.x - a.x);
}
int main()
{
	int n , i , l , r , mid , ret;
	ld a , x = 0 , y , d , ans = 0;
	scanf("%d%Lf" , &n , &d);
	for(i = 1 ; i <= n ; i ++ )
	{
		scanf("%Lf%Lf" , &a , &y) , P = point(O.x + x - d , O.y);
		while(top > 1 && slop(P , sta[top]) < slop(P , sta[top - 1])) top -- ;
		sta[++top] = P;
		O.x -= y + d - x , O.y -= a;
		ret = 1 , l = 2 , r = top;
		while(l <= r)
		{
			mid = (l + r) >> 1;
			if(slop(O , sta[mid]) > slop(O , sta[mid - 1])) ret = mid , l = mid + 1;
			else r = mid - 1;
		}
		ans += slop(O , sta[ret]) , x = y;
	}
	printf("%.0Lf\n" , ans);
	return 0;
}

【bzoj3203】[Sdoi2013]保護出題人 凸包+二分