1. 程式人生 > >[POI2011]Lightning Conductor

[POI2011]Lightning Conductor

script 因此 現在 位置 stdin display printf 區間 cpp

題面在這裏

description

已知一個長度為\(n\)的序列\(a_1,a_2,...,a_n\)

對於每個\(1\le i\le n\),找到最小的非負整數\(p\),
滿足對於任意的\(1\le j\le n\),\(a_j\le a_i+p-\sqrt{|i-j|}\)

data range

\[n\le 5\times 10^5,a_i\le 10^9\]

solution

絕對值怎麽辦?
我們先從左到右\(DP\ j< i\)的部分(此時有\(|i-j|=i-j\)),
再右到左\(DP\ j> i\)的部分(此時有\(|i-j|=j-i\));

那麽我們現在考慮這個式子:
\[f[i]=max_{j=1}^{i-1}(a[j]-a[i]+\sqrt{i-j})\]


對於每一個相同的\(i\),
\(w[j]=\sqrt{i-j}\),那麽我們很容易發現這個函數是凸函數
對於兩個決策\(j<k\),當\((j-k)\)一定時,我們發現\(w[j]-w[k]\)隨著\(i\)的遞增而遞減
而對於這兩個決策,\(j\)\(k\)優當且僅當對於\(j<k<i\le n\),
\[a[j]+\sqrt{i-j}\ge a[k]+\sqrt{i-k}\]

\[g[j,k]=a[j]-a[k]+w[j]-w[k]\ge 0\]
\(i\)遞增時,\(g[j,k]\)這個函數單調遞減,那麽存在一個分界點\(i‘\),
使得當\(i\le i‘\)
時,\(g[j,k]\ge 0\)(\(j\)更優),當\(i\ge i‘\)時,\(g[j,k]\le 0\)(\(k\)更優);

那麽我們知道這個\(DP\)方程具有決策單調性
具體如何實現?

考慮相鄰的兩個決策\(j,k\),
由於上面的性質,我們可以二分出這兩個決策的分界點\(t[j,k]\);
對於相鄰的三個決策\(x,y,z\),如果\(t[x,y]\ge t[y,z]\)那麽決策\(y\)是沒有用的可以扔掉

考慮維護一個單調隊列,存放所有合法的決策;
由於我們在維護的同時\(i\)是變化的,
因此我們需要求出每個決策此時轉移最優的區間
即單調隊列存放的元素為\((pos,l_{pos},r_{pos})\)

(註意\(pos < l_{pos}\le r_{pos}\))

當加入新決策\(j\)時,考慮隊尾的決策\((k,l_k,r_k)\);
如果\(j\)總是比\(k\)要劣(在這裏指對於\(i=n\)仍有\(a[j]+\sqrt{i-j}\le a[k]+\sqrt{i-k}\)),
則不考慮決策\(j\);
否則(此時\(r_j=n\)),如果對於\(i=l_k\)\(a[j]+\sqrt{i-j}\ge a[k]+\sqrt{i-k}\),
一直彈掉隊尾決策並考慮隊尾的前一個決策,如果隊列為空則壓入\((j,j,n)\);
否則(此時存在一個決策\(k\)無法被彈出),考慮二分決策\(k\)\(j\)的分界點\(mid\)
(本人的定義是,對於\(i\le mid\),有\(a[k]+\sqrt{i-k}> a[j]+\sqrt{i-j}\)),
之後將\(r_k\)修改為\(mid\),壓入決策\((j,mid,n)\)

il dd calc(int j,int i){return a[j]-a[i]+sqrt(i-j);}
//計算j轉移至i時對應的DP值
il int divide(int k,int j,int l,int r){
    RG int mid,ans=l;
    while(l<=r){
        mid=(l+r)>>1;
        if(calc(k,mid)>calc(j,mid))
            ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}
//二分分界點
il void push(int i){
    if(calc(Qx[R],n)>calc(i,n))return;
    while(calc(Qx[R],Ql[R])<calc(i,Ql[R])&&L<=R)R--;
    if(L>R){Ql[++R]=i;Qr[R]=n;Qx[R]=i;return;}
    Qr[R]=divide(Qx[R],i,Ql[R],Qr[R]);
    R++;Ql[R]=Qr[R-1]+1;Qr[R]=n;Qx[R]=i;
}
//插入新決策

當要查詢位置\(pos\)時,直接從隊頭開始查找,
如果隊首決策\((k,l_k,r_k)\)\(r_k<pos\)或隊首決策劣於隊首後決策則將其彈出;
否則,將其作為此次查詢的答案

il int pop(int i){
    while(Qr[L]<i||(L<R&&calc(Qx[L],i)<calc(Qx[L+1],i)))L++;
    return Qx[L];
}
//查詢答案

這裏是總的\(DP\)模板

il void DP(int *a,int *p,int n){
    L=0;R=-1;
    for(RG int i=1;i<=n;i++){
        if(i!=1)p[i]=ceil(calc(pop(i),i));Ql[L]=i;push(i);
        //答案為非負整數,這裏做了上取整
    }
}
//DP序列

code

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define mp make_pair
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int N=1000000;
const int inf=1e9+7;
il ll read(){
    RG ll data=0,w=1;RG char ch=getchar();
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘))ch=getchar();
    if(ch==‘-‘)w=-1,ch=getchar();
    while(ch<=‘9‘&&ch>=‘0‘)data=data*10+ch-48,ch=getchar();
    return data*w;
}

il void file(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
}

int n,a[N],p[N],s[N];
int Qx[N],Ql[N],Qr[N],L,R;

il dd calc(int j,int i){return a[j]-a[i]+sqrt(i-j);}
//計算j轉移至i時對應的DP值
il int divide(int k,int j,int l,int r){
    RG int mid,ans=l;
    while(l<=r){
        mid=(l+r)>>1;
        if(calc(k,mid)>calc(j,mid))
            ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}
//二分分界點
il void push(int i){
    if(calc(Qx[R],n)>calc(i,n))return;
    while(calc(Qx[R],Ql[R])<calc(i,Ql[R])&&L<=R)R--;
    if(L>R){Ql[++R]=i;Qr[R]=n;Qx[R]=i;return;}
    Qr[R]=divide(Qx[R],i,Ql[R],Qr[R]);
    R++;Ql[R]=Qr[R-1]+1;Qr[R]=n;Qx[R]=i;
}
//插入新決策
il int pop(int i){
    while(Qr[L]<i||(L<R&&calc(Qx[L],i)<calc(Qx[L+1],i)))L++;
    return Qx[L];
}
//查詢答案
il void DP(int *a,int *p,int n){
    L=0;R=-1;
    for(RG int i=1;i<=n;i++){
        if(i!=1)p[i]=ceil(calc(pop(i),i));Ql[L]=i;push(i);
    }
}
//DP序列
int main()
{
    n=read();
    for(RG int i=1;i<=n;i++)a[i]=read();
    DP(a,p,n);reverse(a+1,a+1+n);DP(a,s,n);
    for(RG int i=1;i<=n;i++)
        printf("%d\n",max(0,max(p[i],s[n-i+1])));
    //記得答案是非負整數
    return 0;
}

[POI2011]Lightning Conductor