1. 程式人生 > >bzoj 2216 [Poi2011]Lightning Conductor

bzoj 2216 [Poi2011]Lightning Conductor

題解

給出一個長度為n的序列a,你需要對於每個i,找到最小的整數p滿足對於 j , a j a

i + p i j
\forall j,a_j\leq a_i+p- \sqrt{|i-j|}

題意

移項,有 p a [ j

] a [ i ] + i j p\geq a[j]-a[i]+\sqrt{|i-j|}
也即是說 p = m a x ( a [ j ] + i j ) a [ i ] p=max(a[j]+\sqrt{|i-j|})-a[i]
如果我們正序倒序都做一遍的話,那麼就是 p = m a x ( a [ j ] + i j ) a [ i ] p=max(a[j]+\sqrt{i-j})-a[i]
於是奇奇怪怪的東西出現了,決策單調性
觀察 i j \sqrt{i-j} ,由於這個本身的增長率是逐漸變小的(可以求導得到),那麼也就是說,當轉移i的時候,若j優於k,且k<j,那麼後面j一定也優於k
也就是說,每個位置作為最優所覆蓋的區間一定是連續的
所以我們就可以用一個單調佇列來實現,更新那些位置作為最優所覆蓋的區間
顯然插入一個新點後,它作為最優所替代的區間是[x,n](當然,也可能是空集),我們就一個個彈掉隊尾裡整體都比它劣的,最後再在隊尾裡二分
然後倒過來再做一次就行了。記得dp陣列也要倒過來。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=500005;
int n;
int a[N];
int q[N],head,tail;
int L[N],R[N];
int d[N];
double calc(int x,int y){
    return a[x]-a[y]+sqrt(abs(x-y));
}
void solve(){
    head=1,tail=0;
    for(int i=1;i<=n;i++){
        while(head<=tail&&R[q[head]]<i)
            head++;
        if(head<=tail){
            L[q[head]]=i;
            d[i]=max(d[i],(int)ceil(calc(q[head],i)));
        }
        if(head>tail||calc(i,n)>calc(q[tail],n)){
            R[i]=n;
            while(head<=tail&&calc(i,L[q[tail]])>=calc(q[tail],L[q[tail]]))
                tail--;
            if(head<=tail){
                int l=L[q[tail]],r=R[q[tail]]+1;
                while(l<r){
                    int mid=(l+r)>>1;
                    if(calc(i,mid)<calc(q[tail],mid))
                        l=mid+1;
                    else
                        r=mid;
                }
                L[i]=l;
                R[q[tail]]=l-1;
            }
        }
        else
            L[i]=i+1;
        q[++tail]=i;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    solve();
    reverse(a+1,a+n+1);
    reverse(d+1,d+n+1);
    solve();
    reverse(d+1,d+n+1);
    for(int i=1;i<=n;i++)
        printf("%d\n",d[i]);
}