1. 程式人生 > >[BZOJ4850][JSOI2016]燈塔(分塊/決策單調性優化DP)

[BZOJ4850][JSOI2016]燈塔(分塊/決策單調性優化DP)

第一種方法是決策單調性優化DP。

決策單調性是指,設i>j,若在某個位置x(x>i)上,決策i比決策j優,那麼在x以後的位置上i都一定比j優。

根號函式是一個典型的具有決策單調性的函式,由於根號函式斜率遞減,所以i決策的貢獻的增長速度必定比j快。

於是使用基礎的決策單調性優化即可。

注意兩個問題,一是DP函式要存實數而不能存整數,因為先取整會丟失在後面的判斷中需要的資訊。二是記錄決策作用區間的時候左端點要實時更新,即下面的p[st].l++,否則在二分時會出現錯誤。

 1 #include<cmath>
 2 #include<cstdio>
 3
#include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 using namespace std; 6 7 const int N=100010; 8 double f[N],g[N]; 9 int n,st,ed,h[N]; 10 struct P{ int l,r,p; }q[N]; 11 12 int Abs(int x){ return (x>0) ? x : -x; } 13 double cal(int x,int y){ return h[x]-h[y]+sqrt(Abs(y-x)); }
14 15 int find(P a,int b){ 16 int L=a.l,R=a.r; 17 while (L<R){ 18 int mid=(L+R)>>1; 19 if (cal(a.p,mid)>=cal(b,mid)) L=mid+1; else R=mid; 20 } 21 return L; 22 } 23 24 void work(double f[]){ 25 st=ed=1; q[1]=(P){1,n,1}; 26 rep(i,2,n){
27 q[st].l++; if (q[st].l>q[st].r) st++; 28 f[i]=cal(q[st].p,i); 29 if (st>ed || (cal(q[ed].p,n)<cal(i,n))){ 30 while (st<=ed && cal(q[ed].p,q[ed].l)<cal(i,q[ed].l)) ed--; 31 if (st>ed) q[++ed]=(P){i,n,i}; 32 else{ 33 int t=find(q[ed],i); q[ed].r=t-1; q[++ed]=(P){t,n,i}; 34 } 35 } 36 } 37 } 38 39 int main(){ 40 scanf("%d",&n); 41 rep(i,1,n) scanf("%d",&h[i]); 42 work(f); reverse(h+1,h+n+1); 43 work(g); reverse(g+1,g+n+1); 44 rep(i,1,n) printf("%d\n",max((int)ceil(max(f[i],g[i])),0)); 45 return 0; 46 }

第二種方法是分塊。

這題中,對於固定的i,sqrt(i-j)只有O(sqrt(n))種取值,而每種取值的區間長度也只有O(sqrt(n))個。

預處理從每個數開始後O(sqrt(n))個數中的最大值,暴力列舉sqrt(i-j)的取值更新答案。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 6 typedef long long ll;
 7 using namespace std;
 8 
 9 const int N=100010,K=620;
10 int n,ans,h[N],mx[N][650];
11 
12 int main(){
13     freopen("bzoj4850.in","r",stdin);
14     freopen("bzoj4850.out","w",stdout);
15     scanf("%d",&n);
16     rep(i,1,n) scanf("%d",&h[i]);
17     rep(i,1,n){
18         mx[i][1]=h[i];
19         rep(j,2,min(K,n-i+1)) mx[i][j]=max(mx[i][j-1],h[i+j-1]);
20     }
21     rep(i,1,n){
22         ans=0;
23         for (int pos=i,j=1,nxt; pos!=1; j++)
24             nxt=pos-1,pos=max(pos-j*2+1,1),ans=max(ans,mx[pos][nxt-pos+1]-h[i]+j);
25         for (int pos,j=1,nxt=i; nxt!=n; j++)
26             pos=nxt+1,nxt=min(nxt+j*2-1,n),ans=max(ans,mx[pos][nxt-pos+1]-h[i]+j);
27         printf("%d\n",ans);
28     }
29     return 0;
30 }