BZOJ3203 [Sdoi2013] 保护出题人

@(BZOJ)[凸包, 三分法]

Description

Description

Input

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

Output

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

Sample Input

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

Sample Output

7

HINT

第一关:距离房子3米处有一只血量3点的僵尸,植物最小攻击力为1.00000; 第二关:距离房子1米处有一只血量1点的僵尸、3米处有血量3点的僵尸,植物最小攻击力为1.33333; 第三关:距离房子8米处有一只血量10点的僵尸、10米处有血量1点的僵尸、12米处有血量3点的僵尸,植物最小攻击力为1.25000; 第四关:距离房子8米处有一只血量4点的僵尸、10米处有血量10点的僵尸、12米处有血量1点的僵尸、14米处有血量3点的僵尸,植物最小攻击力为1.40000; 第五关:距离房子3米处有一只血量2点的僵尸、5米处有血量4点的僵尸、7米处有 血量10点的僵尸、9米处有血量1点的僵尸、11米处有血量3点的僵尸,植物最小攻击力 为2.28571。 植物攻击力的最小总和为7.26905。
对于100%的数据, 1≤n≤105,1≤d≤1012,1≤x≤ 1012,1≤a≤1012

Solution

\(sum_i = sum_{i - 1} + a_i\), 則有對於第\(i\)次攻擊, 有$$f_i \ge max \left( \frac{sum_i - sum_{j - 1}}{x_i + d * (i - j)} \right)(1 \le j \le i)$$
維護一個斜率單調遞增的凸包, 每個點的坐標為\((i*d,sum_{i-1})\). 對於每一個\(i\)用三分法找到凸包中與當前點\((x_i + i * d, sum_i)\)的連線斜率最大的點即可.
注意在整數上三分的方法

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define eps 1e-6
#define LL long long
#define mid1 l+(r-l)/3
#define mid2 l+(r-l)/3*2

const int N=100010;

int n,siz;
double ans,d,sum[N],xi[N];

struct Point
{
	double x,y;
} q[N];

Point operator - (Point x,Point y)
{
	return (Point)
	{
		x.x-y.x, x.y-y.y
	};
}

inline double Cross(Point x,Point y)
{
	return x.x*y.y-x.y*y.x;
}

inline double calc(Point x)
{
	int l=1,r=siz,i;
	double maxn=0,k1,k2;
	
	while(l+2<r)
		{
			k1=(x.y-q[mid1].y)/(x.x-q[mid1].x);
			k2=(x.y-q[mid2].y)/(x.x-q[mid2].x);
			maxn=max(maxn,max(k1,k2));
			if(k1<k2) 
				l=mid1;
			else 
				r=mid2;
		}
		
	for(i=l; i<=r; ++i) 
		maxn=max(maxn,(x.y-q[i].y)/(x.x-q[i].x));
	return maxn;
}

int main()
{
	int i,j;
	scanf("%d%lfd",&n,&d);
	
	for(i=1; i<=n; ++i)
		{
			scanf("%lf%lf",&sum[i],&xi[i]);
			sum[i]+=sum[i-1];
		}
		
	ans=sum[1]/xi[1];
	q[siz=1]=(Point)
	{
		d,sum[0]
	};
	
	for(i=2; i<=n; ++i)
		{
			Point x=(Point)
			{
				i*d,sum[i-1]
			};
			
			while(siz>=2&&Cross(q[siz]-q[siz-1], x-q[siz-1])<=0) 
				--siz;
				
			q[++siz]=x;
			x=(Point)
			{
				xi[i]+i*d, sum[i]
			};
			ans+=calc(x);
		}
		
	printf("%.0f\n",ans);
}
posted @ 2017-02-22 08:10  Zeonfai  阅读(245)  评论(0编辑  收藏  举报