BZOJ2216 Poi2011 Lightning Conductor 【决策单调性优化DP】

Description

已知一个长度为n的序列a1,a2,...,an。
对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p - sqrt(abs(i-j))

Input

第一行n,(1<=n<=500000)
下面每行一个整数,其中第i行是ai。(0<=ai<=1000000000)

Output

n行,第i行表示对于i,得到的p

Sample Input

6
5
3
2
4
2
4

Sample Output

2
3
5
3
5
4


决策单调性优化DP
。。。调了一晚上,都要自闭了


首先可以发现根号函数是一个增长越来越慢的函数
所以一个点如果不能对答案造成贡献,那么他永远都不能造成贡献了
然后就考虑怎么维护这个东西
首先正反跑两遍然后取max这个思路非常显然
现在可以考虑怎么维护单次的贡献
因为对于i,需要最大化
\(h_j - h_i + sqrt(i-j)\)
所以我们可以把原序列划分成一些区间
使得这些区间可以取到最大值
然后每次统计贡献直接在区间上查找就可以了
考虑维护一个队列来维护\([i,n]\)这个区间的所有最优区间
每个节点用三元组\([l,r,pos]\)表示
代表在\([l,r]\)这个区间里面pos可以贡献最大值
然后直接每次看一看如果当前新加入的节点是可以完全包含末尾区间,直接弹出,知道不能操作
这时如果序列为空直接加入\([i,n,i]\)
否则把最后一个区间划分成两个区间进行操作就可以了


注意统计贡献的时候参数顺序很重要


//Author: dream_maker
#include<bits/stdc++.h>
using namespace std;
//----------------------------------------------
//typename
typedef long long ll;
//convenient for
#define for_up(a, b, c) for (int a = b; a <= c; ++a)
#define for_down(a, b, c) for (int a = b; a >= c; --a)
#define for_vector(a, b) for (int a = 0; a < (signed)b.size(); ++a)
//inf of different typename
const int INF_of_int = 1e9;
const ll INF_of_ll = 1e18;
//fast read and write
template <typename T>
void Read(T &x) {
  bool w = 1;x = 0;
  char c = getchar();
  while (!isdigit(c) && c != '-')c = getchar();
  if (c == '-')w = 0, c = getchar();
  while (isdigit(c)) {
    x = (x<<1) + (x<<3) + c -'0';
    c = getchar();
  }
  if (!w) x = -x;
}
template <typename T>
void Write(T x) {
  if (x < 0) {
    putchar('-');
    x = -x; 
  }
  if (x > 9)Write(x / 10);
  putchar(x % 10 + '0');
}
//----------------------------------------------
const int N = 500010;
const double eps = 1e-5;
int n, l, r;
int dp[N] = {0}, h[N];
struct Node {
  int l, r, pos;
  Node(){}
  Node(int l, int r, int pos):l(l), r(r), pos(pos){}
}p[N];
double cal(int a, int b) { // 一定注意顺序 是统计 a覆盖b的最小高度
  return (double)h[b] + sqrt(abs(a - b)) - (double)h[a];
}
int find(Node t, int x) {
  int l = t.l, r = t.r, ans = 0;
  while (l <= r) {
    int mid = (l + r) >> 1;
    if(cal(mid, t.pos) <= cal(mid, x)) r = mid - 1, ans = mid;
    else l = mid + 1;
  }
  return ans;
}
void solve(bool typ) {
  int l = 1, r = 0;
  for_up(i, 1, n) {
    if (l <= r && ++p[l].l > p[l].r) ++l;
    if (l > r || cal(n, i) > cal(n, p[r].pos)) {
      // p[r] 无论如何不会比i优 直接弹掉
      while (l <= r && cal(p[r].l, p[r].pos) < cal(p[r].l, i)) --r;
      int now = (l > r) ? i : find(p[r], i);
      if (l <= r) p[r].r = now - 1;// 如果上一个区间存在 更新右端点
      p[++r] = (Node){now, n, i};
    }
    int id = typ ? n - i + 1 : i;
    dp[id] = max(dp[id], (int)ceil(cal(i, p[l].pos)));
  }
}
int main() {
  Read(n);
  for_up(i, 1, n) Read(h[i]);
  solve(0);
  reverse(h + 1, h + n + 1);
  solve(1);
  for_up(i, 1, n) {
    Write(dp[i]);
    putchar('\n');
  }
  return 0;
}

转载于:https://www.cnblogs.com/dream-maker-yk/p/9727010.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值