BZOJ2216[Poi2011]Lightning Conductor

题意

给出长度为 n 的序列 {hn} 对于每一个 i 求最小的 p 满足任意的 j 都有

hjhi+pij

思路

现在对于这一道题,定义 gi=maxnk=1(hk+ik)

先把绝对值拆开,分类讨论

观察到 ik 是单调递减的,所以这个东西是具有决策单调性的,也就是说对于点 i,j(i<j) 的决策点 ki,kj 一定有 kikj

定义 Solve(l,r,L,R) 为当前待 DP 的区间是 [l,r] 此时的最优决策区间是 [L,R]

我们先暴力 DP 出 l,r 的中点的 DP 值,同时记录最优决策点的位置 pos ,那么对于 mid 之后的点的决策肯定不会越过 pos , pos 相当于分界点,最优决策区间分成两端。

由于 mid 的 DP 值已经求出来了,所以不需要算,继续递归到

Solve(l,mid1,L,pos),Solve(mid+1,r,pos,R)

对于每一个最优决策在分治树上只会出现 log 次,所以时间复杂度为 O(nlogn)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

template <typename Tp>Tp Max(const Tp &a, const Tp &b) {return a > b ? a : b;}
template <typename Tp>Tp Min(const Tp &a, const Tp &b) {return a < b ? a : b;}
template <typename Tp>Tp Abs(const Tp &a) {return a > 0 ? a : -a;}

template <typename Tp>void Read(Tp &x) {
    Tp in = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') f = -1; ch = getchar();}
    while(ch>='0' && ch<='9') {in = in*10+ch-'0'; ch = getchar();}
    x = in * f;
}

typedef long long LL;

const int SN = 500000 + 10;

const int inf = 0x3f3f3f3f;

double f[SN], g[SN];

LL h[SN], pos[SN], n;

double Calc(int x, int y) {
    return h[x] + sqrt(Abs(x - y)) - h[y];
}

void CDQ(int l, int r, int L, int R, double *f) {
    if(l > r) return ;

    int mid = (l + r) >> 1;

    int limits = Min(mid, R);

    int bet = limits;

    f[pos[mid]] = 0;

    for(int i = L; i <= limits; i++) 
    if(Calc(i, mid) > f[pos[mid]])
        f[pos[mid]] = Calc(i, mid), bet = i;

    CDQ(l, mid - 1, L, bet, f), CDQ(mid + 1, r, bet, R, f);
}

// mid 已经计算过了,只需要递归不含 mid 的两边即可

int main() {    
    Read(n);

    for(int i = 1; i <= n; i++) Read(h[i]);

    for(int i = 1; i <= n; i++) pos[i] = i;

    CDQ(1, n, 1, n, f);

    for(int i = 1; i <= n / 2; i++)
    std::swap(h[i], h[n - i + 1]), std::swap(pos[i], pos[n - i + 1]);

    CDQ(1, n, 1, n, g);

    for(int i = 1; i <= n; i++)
    printf("%d\n", (int) ceil(Max(g[i], f[i])));

    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值