bzoj 2216 [Poi2011]Lightning Conductor
阿新 • • 發佈:2019-01-06
題解
給出一個長度為n的序列a,你需要對於每個i,找到最小的整數p滿足對於
題意
移項,有
也即是說
如果我們正序倒序都做一遍的話,那麼就是
於是奇奇怪怪的東西出現了,決策單調性
觀察
,由於這個本身的增長率是逐漸變小的(可以求導得到),那麼也就是說,當轉移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]);
}