bzoj2216 [Poi2011]Lightning Conductor(決策單調性DP)
阿新 • • 發佈:2019-01-25
bzoj2216 [Poi2011]Lightning Conductor
題意:
已知一個長度為n的序列a1,a2,…,an。
對於每個1<=i<=n,找到最小的非負整數p滿足 對於任意的j, aj < = ai + p - sqrt(abs(i-j))
資料範圍
1<=n<=500000,0<=ai<=1000000000
題解:
好題。
對於abs的處理就直接正反各跑一遍。
決策與無關。
因為這東西是漲得越來越慢的,決策點是單調的。
(於是傻逼的我想都沒想就直接佇列首尾彈一彈以為能O(n)然後並不行。
因為在i移動時,佇列中的點的大小關係可能發生變化,不知道什麼時候該彈。
反過來思考,每個點對其後方的點的影響,以它作為答案的是連續的一段區間。
需要求出每個點準確的能夠控制的區間。)
於是在加入點時計算出其能夠控制的區間,這個過程用二分,以便彈出。
注意,過程中必須要保證L>=i,
否則由於不能保證j< i,sqrt()誰漲得快不一定,下面彈棧不能那麼彈 。
UPD:整體二分好啊,好寫好調,哪像單調佇列
程式碼:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=500005;
const int inf=0x3f3f3f3f;
struct node
{
int L,R,id;
node(){}
node(int L,int R,int id):L(L),R(R),id(id){}
}Q[N];
int a[N],f[N],n;
double cal(int j,int i) {return (double)a[j]-a[i]+sqrt (abs(i-j));}
int find(int x,int y,int l,int r)
{
int lf=l; int rg=r;
while(lf+1<rg)
{
int mid=(lf+rg)>>1;
if(cal(x,mid)<cal(y,mid)) lf=mid;
else rg=mid;
}
if(cal(x,rg)<cal(y,rg)) return rg;
else return lf;
}
void getans()
{
int h=1,t=0;
for(int i=1;i<=n;i++)
{
Q[h].L++;
while(h<=t&&(Q[h].R<i||Q[h].L>Q[h].R)) h++;
if(h>t) Q[++t]=node(i,n,i);
else if(cal(i,n)>cal(Q[t].id,n))
{
while(h<=t&&cal(i,Q[t].L)>=cal(Q[t].id,Q[t].L)) t--;
if(h<=t)
{
int pos=find(i,Q[t].id,Q[t].L,Q[t].R);
Q[t].R=pos; Q[++t]=node(pos+1,n,i);
}
else Q[++t]=node(i,n,i);
}
f[i]=max(f[i],(int)ceil(cal(Q[h].id,i)));
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
memset(f,0,sizeof(f));
getans();
for(int i=1;i+i<=n;i++) {swap(a[i],a[n-i+1]); swap(f[i],f[n-i+1]);}
getans();
for(int i=1;i<=n;i++) printf("%d\n",f[n-i+1]);
return 0;
}