[POI2011]Lightning Conductor 决策优化

44 篇文章 0 订阅
41 篇文章 1 订阅
题目大意:

给定一个长度为 n n n的序列 { a n } \{a_n\} {an},对于每个 i ∈ [ 1 , n ] i \in [1,n] i[1,n],求出一个最小的非负整数 p p p,是的 ∀ j ∈ [ 1 , n ] \forall j \in [1,n] j[1,n],都有 a j ≤ a i + p − ∣ i − j ∣ a_j \le a_i+p-\sqrt{|i-j|} ajai+pij
1 ≤ n ≤ 5 e 5 , 0 ≤ a i ≤ 1 e 9 1 \le n \le 5e5, 0 \le a_i \le 1e9 1n5e5,0ai1e9

解题思路:
  • 式子可以转换为: a j + ∣ i − j ∣ − a i ≤ p a_j +\sqrt{|i-j|} - a_i \le p aj+ij aip
  • 通过暴力打表可以发现对于每个 p i p_i pi转移的 j i j_i ji都是递增的,所以可以通过决策单调性来优化
  • 而因为 ∣ i − j ∣ \sqrt{|i-j|} ij 的增长速度是逐渐下降的,所以可能存在某个 j 2 > j 1 j_2>j_1 j2>j1,在到某个 i i i之后 a [ j 2 ] + i − j 2 > a [ j 1 ] + i − j 1 a[j_2]+\sqrt{i-j_2} > a[j_1]+\sqrt{i-j_1} a[j2]+ij2 >a[j1]+ij1 (原本是 j 1 j_1 j1 j 2 j_2 j2优秀)
  • 所以对于队列里相邻的点,可以通过二分来找靠后的点什么时候比前者更优,注意这些点也要单调递增的
AC代码:
//因为本题的转移代价是sqrt(i-j)而该函数的增长速度是逐渐下降的,
//所以可能存在某个j2>j1,在到某个i之后a[j2]+sqrt(i-j2) > a[j1]+sqrt(i-j1)(原本是小于的)
#include <bits/stdc++.h>
#define ft first
#define sd second
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 5e5 + 10;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
int n, a[maxn], ans[maxn];
int q[maxn], k[maxn];
int find(int x, int y) {
    int lc = y, rc = n, mc, res = n + 1;
    while (lc <= rc) {
        mc = (lc + rc) >> 1;
        if (a[x] + sqrt(mc - x) <= a[y] + sqrt(mc - y))
            res = mc, rc = mc - 1;
        else
            lc = mc + 1;
    }
    return res;
}
//k[i]放的是队列中第i个点与队列中的第i+1个点,什么时候第i个点的函数曲线会被第i+1个点超过
//队列中的k是单调递增的,如果存在一个i<i+1,但k(i)>k(i+1),意思就是函数曲线第i个点被i+1超过时,i+2早就超过了i+1,则i+1是无用的点
void solve() {
    for (int i = 1, h = 1, t = 0; i <= n; i++) {
        while (h < t && k[t - 1] > find(q[t], i))//保证单调递增,及判断t-1被t超过时,t不能被i超过
            t--;
        k[t] = find(q[t], i); //利用二分查找来得到k
        q[++t] = i;
        while (h < t && k[h] <= i) h++; //队头的k此时已经小过i了,所以弹出
        ans[i] = max(ans[i], (int)(-a[i] + a[q[h]] + ceil(sqrt(i - q[h]))));
    }
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    solve();
    for (int i = 1; i <= n / 2; i++)
        swap(a[i], a[n - i + 1]), swap(ans[i], ans[n - i + 1]);
    solve();
    for (int i = n; i >= 1; i--)
        cout << ans[i] << endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值