bzoj3203【SDOI2013】保护出题人

3203: [Sdoi2013]保护出题人

Time Limit: 3 Sec   Memory Limit: 128 MB
Submit: 533   Solved: 281
[ Submit][ Status][ Discuss]

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≤10^5,1≤d≤10^12,1≤x≤ 10^12,1≤a≤10^12




凸包+三分,思路好

sum[i]表示a[i]的前缀和。

可以把每一个僵尸和他前面僵尸的力量叠加起来考虑,这样每个僵尸从一开始就是受伤害的,而他必须在靠近房子之前被打死。

所以有:y[i]=max{(sum[i]-sum[j-1])/(x[i]+(i-j)*d)},j≤i。

如果暴力计算是n^2的,显然不能过。

这里有一个思路很好的优化:

我们看这个公式,发现很想斜率的形式。令y1=sum[i],y2=sum[j-1],x1=x[i]+i*d,y2=j*d,k=(y1-y2)/(x1-x2)。所以我们就是要求P(x1,y1)和Q(x2,y2)连线斜率的最大值。

每一关的P点是不一样的,但Q点是定值。

于是问题变成每次加入一个点,然后给出一个P点,询问P点和这些点的连线斜率的最大值。因为P一定在这些点右上方,所以维护一个下凸包,每次在凸包上三分即可。

注意整数三分和实数三分写法的不同:while(r-l>=3) ……




#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 100005
#define inf 1e30
using namespace std;
int n,head,tail,q[maxn];
ll d,sum[maxn],a[maxn];
double ans;
inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
double getk(int x,int y)
{
	return (double)(sum[x-1]-sum[y-1])/(double)(d*x-d*y);
}
double getk2(int x,int y)
{
	return (double)(sum[x]-sum[y-1])/(double)(a[x]+d*x-d*y);
}
double sanfen(int x)
{
	int l=head,r=tail,lmid,rmid;
	while (r-l>=3)//注意整数三分与实数三分的区别 
	{
		lmid=l+(r-l)/3;rmid=r-(r-l)/3;
		if (getk2(x,q[lmid])<getk2(x,q[rmid])) l=lmid;
		else r=rmid;
	}
	double ret=-inf;
	F(i,l,r) ret=max(ret,getk2(x,q[i]));
	return ret;
}
int main()
{
	n=read();d=read();
	F(i,1,n) sum[i]=sum[i-1]+read(),a[i]=read();
	head=1;tail=0;
	F(i,1,n)
	{
		while (head<tail&&getk(q[tail-1],q[tail])>getk(q[tail],i)) tail--;
		q[++tail]=i;
		ans+=sanfen(i);
	}
	printf("%.0lf\n",ans);
	return 0;
}


你可能感兴趣的:(凸包,三分,bzoj)