P3515 [POI2011]Lightning Conductor

发布时间 2023-04-09 00:13:45作者: 腾云今天首飞了吗

给定一个长度为 \(n\) 的序列 \(\{a_n\}\),对于每个 \(i\in [1,n]\) ,求出一个最小的非负整数 \(p\) ,使得 \(\forall j\in[1,n]\),都有 \(a_j\le a_i+p-\sqrt{|i-j|}\)
\(1 \le n \le 5\times 10^{5}\)\(0 \le a_i \le 10^{9}\)

第一次遇到的决策单调性优化dp……也是多少带点神奇了,下面浅析一下。

四边形不等式

\(w(x,y)\) 是定义在整数集合上的二元函数。若对于定义域上的任意整数 \(a\),\(b\),\(c\),\(d\),其中 \(a≤b≤c≤d\),都有 \(w(a,d)+w(b,c)≥w(a,c)+w(b,d)\) 成立,则称函数 \(w\) 满足四边形不等式。

\(w(x,y)\) 是定义在整数集合上的二元函数。若对于定义域上的任意整数 \(a\),\(b\),其中 \(a<b\),都有 \(w(a,b+1)+w(a+1,b)≥w(a,b)+w(a+1,b+1)\) 成立,则函数 \(w\) 满足四边形不等式。

决策单调性

假如对于某一个dp方程,\(dp[i]\) 的最优转移是 \(dp[k]\),那么称 \(k\)\(i\) 的决策点。
而dp方程满足决策单调性指的是,决策点 \(k\) 随着 \(i\) 的增大保持单调不减。

定理:在状态转移方程 \(f[i]=min{f[j]+w(j,i)},j∈[0,i)\) 中,若函数 \(w\) 满足四边形不等式,则 \(f\) 具有决策单调性。

上述结论具体的证明,先挖个坑?

可见,如果一个dp方程满足决策单调性,我们就可以利用这个单调性,在转移的过程中在更小的范围中寻找最优转移,从而避免了 \(O(n)\) 枚举,达到了优化的目的。

利用决策单调性,主要有分治单调队列两种方法来优化 \(dp\)

法一:单调队列

每次求得一个新的 \(dp[i]\) 时,思考 \(dp[i]\) 可以作为哪些状态的最优决策。由于决策是单调的,因此总是存在一个 \(pos\),使得 \(pos\) 以前的状态对应的最优决策的位置小于 \(i\),而在 \(pos\) 之后的状态对应的最优决策的位置大于或等于 \(i\)。那么,如何通过单调队列来维护这些决策呢?

用单调队列来维护决策集合 \(p\),存一个三元组 \(k,l,r\) 分别表示 \(l~r\) 之间的所有状态对应的最优决策都是 \(dp[k]\)

维护过程:

  • 令队头存在的三元组为 \((k_0,l_0,r_0)\),假如 \(r_0\)\(i-1\),直接把队首元素弹出。否则把 \(l_0\) 赋值为 \(i\)
  • 取队首元素记录的决策信息直接转移,算出 \(dp[i]\)
  • \(dp[i]\) 加入决策队列中,步骤如下:
  1. 取出队尾,令其为 \((k_t,l_t,r_t)\)
  2. 若对于 \(f[l_t]\) 来说,\(i\) 是比 \(k_t\) 更优的决策,即 \(f[i]+w(i,l_t)≤f[k_t]+w(k_t,l_t)\),记 \(pos=l\),删除队尾,返回步骤1。
  3. 若对于 \(f[r t]\) 来说,\(i\) 是比 \(k_t\) 更优的决策,即 \(f[k_t]+w(j_t,r_t)≤f[i]+w(i,r_t)\),去往步骤5。
  4. 否则,则在 \([l t,r t]\) 上二分查找出位置 \(pos\),在此之前决策比 \(i\) 优,在此之后决策 \(i\) 更优,将 \([l t,r t]\) 修改为 \([l t,pos−1]\),去往步骤5。
  5. 把三元组 \((i,pos,n)\) 插入队尾。
    ——参考于洛谷用户 \(HoshiuZ\)

复杂度为 \(O(n\log n)\)

法二:分治

分治比单调队列要好想很多,常受本人青睐,下面介绍一下其步骤。

假设当前处理的区间为 \(L~R\),该区间对应的最优决策所处的区间在 \(l~r\) 之间。
\(mid = (L+R) / 2\),然后直接暴力枚举该区间对应的最优决策所处区间的元素,求出 \(dp[mid]\) 对应的最优决策,令其为 \(nr\)
又由于决策具有单调性,所以 \(mid+1~R\) 这一段区间对应的最优决策所处区间一定在 \(nr~r\) 之间,\(L~mid-1\) 这段区间对应的最优决策所处区间一定在 \(l~nr\) 之间。
对左右两个区间递归操作即可。

前置知识介绍完了,那么这个题如何处理呢?
对题目中的式子进行转化,可得

\[a_j + \sqrt{|i-j|} - a_i \le p \]

去掉绝对值,即有:

\[p = \max(a_j +\sqrt{i-j}) - a_i,i >= j \]

\[p = \max(a_j +\sqrt{j - i}) - a_i,i < j \]

好像四边形不等式能证明这是满足决策单调性的……留个坑,下周来填。

在知道了dp式子是满足决策单调性的前提下,只需要正反各dp一次,然后求max就行了。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 6e5;
long long a[MAXN + 5],n;
double f1[MAXN + 5],f2[MAXN + 5];
void work1(int L,int R,int l,int r){//范围+决策范围,思路可参考上述介绍的通过分治利用决策单调性。这里是正着求一边
    if(L > R)return;
    double now = 0;
    int mid = (L + R) / 2,nr = 0;
    f1[mid] = a[mid];
    for(int i = l; i <= min(mid,r); i++){//暴力求dp[mid]的最优决策点
        now = a[i] + sqrt(double(mid - i));
        if(now > f1[mid])f1[mid] = now,nr = i;
    }
    if(!nr)nr = mid;
    f1[mid] -= a[mid];
    work1(L,mid - 1,l,nr);//递归处理子区间
    work1(mid + 1,R,nr,r);
}
void work2(int L,int R,int l,int r){//反着求一遍
    if(L > R)return;
    double now = 0;
    int mid = (L + R) / 2,nr = 0;
    f2[mid] = a[mid];
    for(int i = r; i >= max(l,mid); i--){
        now = a[i] + sqrt(double(i - mid));
        if(now > f2[mid])f2[mid] = now,nr = i;
    }
    if(!nr)nr = mid;
    f2[mid] -= a[mid];
    work2(L,mid - 1,l,nr);
    work2(mid + 1,R,nr,r);
}
int main(){
    scanf("%lld",&n);
    for(int i = 1; i <= n; i++)scanf("%lld",&a[i]);
    work1(1,n,1,n);
    work2(1,n,1,n);
    for(int i = 1; i <= n; i++){
        long long ans = 0;
        ans = ceil(max(f1[i],f2[i]));
        cout << ans << "\n";
    }
}