[SDOI2013]保护出题人

1 篇文章 0 订阅
1 篇文章 0 订阅

题目

题解

数据确实水,n2 算法能过1e4就算了,1e5都能过一组,服了
研究题目与样例数据,我们得知一个重要信息,僵尸死了之后,多余的伤害会传到后面去。打死最前面的僵尸才能打后面,僵尸每秒都移动,那么对于第i个僵尸,我们可以将前面的僵尸与i本身绑在一起(生命值之和),求此时需要多少攻击力,即生命值之和除以i离红线的距离
那么我们轻松可以得到f[i] = max(sum[i] - sum[j - 1]) / (d * (i - j) + x[i])。
f[i]表示第i关需要最小攻击力
最后加起来便是答案
先给一个朴素的代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<ctime>
using namespace std;
int n;
long long sum[100005],a[100005],x[100005],d;
double ans;
long long read(){
	long long x = 0,f = 1;
	char s = getchar();
	while (s < '0' || s > '9'){
		if (s == '-')
			f = -1;
		s = getchar();
	}
	while (s >= '0' && s <= '9'){
		x = x * 10 + s - '0';
		s = getchar();
	}
	return f * x;
}
int main(){
	scanf ("%d%lld",&n,&d);
	for (int i = 1;i <= n;i ++)
		a[i] = read(),x[i] = read();
	for (int i = 1;i <= n;i ++){
		for (int j = i;j >= 2;j --)
			sum[j] = sum[j - 1] + a[i];
		sum[1] = a[i];
		double maxn = 0;
		for (int j = 1;j <= i;j ++)
			maxn = max(maxn,1.0 * sum[j] / (d * (j - 1) + x[i]));
		ans += maxn;
	}
	printf("%.0lf\n",ans);
}

但这个过不了,我们再观察式子,发现是这(x[i]+d*i,sum[i]),(d * j,sum[j - 1])两点的斜率。所以维护一个下凸包,二分求最大斜率即可
我太垃圾了,放一个巨佬博客

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<ctime>
using namespace std;
struct node{
	long long  x,y;
	node(){};
	node(long long X,long long Y){
		x = X,y = Y;
	}
}Q[100005];
int n,cnt;
long long sum[100005],a[100005],x[100005],d;
double ans;
double get(node a,node b){
	return 1.0*(a.y - b.y) / (a.x - b.x);
}
int main(){
	scanf ("%d%lld",&n,&d);
	for (int i = 1;i <= n;i ++)
		scanf ("%lld%lld",&a[i],&x[i]);
	for (int i = 1;i <= n;i ++)
		sum[i] = sum[i - 1] + a[i];
	Q[0] = node(0,0);
	for (int i = 1;i <= n;i ++){
		node tmp = node(i * d,sum[i - 1]);
		while (cnt && get(Q[cnt - 1],Q[cnt]) > get(Q[cnt],tmp))
			cnt --;
		Q[++ cnt] = tmp;
		tmp = node(i * d + x[i],sum[i]);
		int l = 1,r = cnt,pos = 0;
		while (l <= r){
			int mid = (l + r) / 2;
			if (get(Q[mid],tmp) > get(Q[mid - 1],tmp)){
				pos = mid;
				l = mid + 1;
			}
			else 
				r = mid - 1;
		}
		ans += get(Q[pos],tmp);
	}
	printf("%.0lf\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值