[BZOJ3203][SDOI2013][凸包][三分]保护出题人

[Problem Description]

[Algorithm]
凸包 三分
[Analysis]
题目看着非常蛋疼,但是只要读懂了题目,题意还是非常简洁明了的。设sum[i]为前i天所有僵尸的血量和。则由题意可得第i天植物塔防的攻击最小值
f[i] = max((s[i] - s[j - 1]) / (x[i] + d * i - d * j)) 1 <= j <= i
可以把f[i]看成是(d * j, s[j - 1]) 与 (x[i] + d * i, s[i])的连线的斜率。由于做i的决策的时候,(x[i] + d * i, s[i])是一个定点并且这个点一定在所以用来提供决策的点的右面,所以要选取的点一定在下凸壳上。由于d * j是单调的,所以可以用一个单调栈来维护下凸壳。然后通过三分来找最大的斜率。
[Pay Attention]
1.精度问题……刚开始设了一个EPS到1e-5结果挂了……改成long double
2.三分……没大写过三分结果不是死循环就是wa……整数三分要这样写:
while (left + 1 < right)
{
     int lmid = left - 1 + (right - left + 1) / 3;
     int rmid = left - 1 + (right - left + 1) * 2 / 3;
     if (-----)
         left = lmid + 1;
     else
         right = rmid - 1;
}
ans = max(--[left], --[right]);
[Code]
/**************************************************************
    Problem: 3203
    User: gaotianyu1350
    Language: C++
    Result: Accepted
    Time:496 ms
    Memory:4432 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
using namespace std;
 
const long double EPS = 1e-15;
const long long MAXN = 101000;
 
long long st[MAXN] = {0}, top = 0;
long long sum[MAXN] = {0}, x[MAXN], a[MAXN];
long long d, n;
 
inline long double dmax(long double a, long double b)
{
    return a > b ? a : b;
}
 
inline long double xie(long long i, long long j)
{
    long long X1 = d * i; 
    long long Y1 = sum[i - 1];
    long long X2 = d * j;
    long long Y2 = sum[j - 1];
    return (long double)(Y1 - Y2) / (X1 - X2);
}
 
inline long double calc(long long i, long long j)
{
    return (long double)(sum[i] - sum[j - 1]) / (x[i] + d * i - d * j);
}
 
inline long long dcmp(long double a, long double b)
{
    if (fabs(a - b) < EPS) return 0;
    return a > b ? 1 : -1;
}
 
int main()
{
    scanf("%lld%lld", &n, &d);
    for (long long i = 1; i <= n; i++)
    {
        scanf("%lld%lld", &a[i], &x[i]);
        sum[i] = sum[i - 1] + a[i];
    }
    long double ans = 0;
    for (long long i = 1; i <= n; i++)
    {
        while (top > 1 && dcmp(xie(i, st[top]), xie(st[top], st[top - 1])) <= 0)
            top--;
        st[++top] = i;
        long long left = 1, right = top;
        while (left + 1 < right)
        {
            long long t = (right - left + 1) / 3;
            long long l = left - 1 + t;
            long long r = l + t;
            long double lans = calc(i, st[l]);
            long double rans = calc(i, st[r]);
            if (dcmp(lans, rans) < 0)
                left = l + 1;
            else
                right = r - 1;
        }
        ans += dmax(calc(i, st[left]), calc(i, st[right]));
    }
    printf("%.0f\n", (double)ans);   
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值