当前位置: 代码迷 >> 综合 >> 『分治法优化DP』[POI2011]Lightning Conductor
  详细解决方案

『分治法优化DP』[POI2011]Lightning Conductor

热度:1   发布时间:2023-12-17 10:46:31.0

P r o b l e m \mathrm{Problem} Problem

给定一个长度为 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|} aj?ai?+p?i?j ?

1 ≤ n ≤ 5 × 1 0 5 1 \le n \le 5\times 10^{5} 1n5×105 0 ≤ a i ≤ 1 0 9 0 \le a_i \le 10^{9} 0ai?109


S o l u t i o n \mathrm{Solution} Solution

如果我们设 f i f_i fi? 表示当前数字 i i i的答案,那么显然有方程: f i = max ? ( f j + ∣ i ? j ∣ ? f i ) f_i=\max(f_j+\sqrt{|i-j|}-f_i) fi?=max(fj?+i?j ??fi?)
那么我们将方程内的绝对值拆开并分类讨论,则有: f i = max ? ( f j + i ? j ? f j ) ( j ≤ i ) f_i=\max(f_j+\sqrt{i-j}-f_j)(j\le i) fi?=max(fj?+i?j ??fj?)(ji)
f i = max ? ( f j + j ? i ? f j ) ( i ≤ j ) f_i=\max(f_j+\sqrt{j-i}-f_j)(i\le j) fi?=max(fj?+j?i ??fj?)(ij)

那么我们就将题目转化为了两个问题,我们通过达标发现:

  • 第一个状态转移方程的决策点单调不下降。
  • 第二个状态转移方程的决策点单调不上升。

观察到 { f i } \{ f_i \} { fi?}的转移线性无关。我们考虑分治法来优化DP。

函数 c a l c ( l , r , L , R ) \mathrm{calc(l,r,L,R)} calc(l,r,L,R)表示我们需要转移 [ l , r ] [l,r] [l,r]内的状态,其中可选的决策是 [ L , R ] [L,R] [L,R]

  • 每一次取出中点,找到中点的决策点 p p p
  • 递归查找 c a l c ( l , m i d ? 1 , L , p ) \mathrm{calc(l,mid-1,L,p)} calc(l,mid?1,L,p) c a l c ( m i d + 1 , r , p , R ) \mathrm{calc(mid+1,r,p,R)} calc(mid+1,r,p,R)

这样,我们就优化掉了一重 O ( n ) O(n) O(n)的复杂度,总复杂度为 O ( n l o g n ) . O(nlogn). O(nlogn).


C o d e \mathrm{Code} Code

#include <bits/stdc++.h> using namespace std;
const int N = 6e5;int n;
double a[N], f1[N], f2[N];int read(void)
{
    int s = 0, w = 0; char c = getchar();while (c < '0' || c > '9') w |= c == '-', c = getchar();while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();return w ? -s : s;
}void Solve1(int l, int r, int ll, int rr)
{
    if (l > r) return;int p = 0, mid = l + r >> 1;double res = 0;for (int i=ll;i<=min(mid,rr);++i){
    double val = a[i] + sqrt(mid - i) - a[mid];if (val > f1[mid]) f1[mid] = val, p = i;}Solve1(l, mid-1, ll, p);Solve1(mid+1, r, p, rr);return;
}void Solve2(int l, int r, int ll, int rr)
{
    if (l > r) return;int p = 0, mid = l + r >> 1;double res = 0;for (int i=max(ll,mid);i<=rr;++i){
    double val = a[i] + sqrt(i - mid) - a[mid];if (val > f2[mid]) f2[mid] = val, p = i;}Solve2(l, mid-1, ll, p);Solve2(mid+1, r, p, rr);return;
}int main(void)
{
    n = read();for (int i=1;i<=n;++i) a[i] = 1.0 * read();Solve1(1, n, 1, n);Solve2(1, n, 1, n);for (int i=1;i<=n;++i) printf("%.0lf\n", ceil(max(f1[i], f2[i])));return 0;
}