BZOJ2448 : 挖油

$f[i][j]$表示仅考虑$[i,j]$区间的答案,则

$f[i][j]=min(max(f[i][k-1],f[k+1][j])+a[k]),ileq kleq j$

维护出$max$的分界点后用一堆线段树维护即可。

时间复杂度$O(n^2log n)$。

#include<cstdio>
const int N=2005,inf=~0U>>1;
int M,n,i,j,a[N],g,f[N][N];
inline void up(int&a,int b){if(a>b)a=b;}
struct ZKW{
int v[4100];
inline void ask(int x,int y,int&t){
  if(x>y)return;
  for(x+=M-1,y+=M+1;x^y^1;x>>=1,y>>=1){
    if(~x&1)up(t,v[x^1]);
    if(y&1)up(t,v[y^1]);
  }
}
inline void ins(int x,int y){for(x+=M;x;x>>=1)up(v[x],y);}
inline void build(){for(int i=1;i<=n+M;i++)v[i]=inf;}
}T1[N],T2[N];
inline void add(int x,int y){
  if(x>1)T1[y].ins(x-1,f[x][y]+a[x-1]);
  if(y<n)T2[x].ins(y+1,f[x][y]+a[y+1]);
}
int main(){
  scanf("%d",&n);
  for(M=1;M<n+2;M<<=1);
  for(i=1;i<=n;i++)scanf("%d",&a[i]),T1[i].build(),T2[i].build();
  for(i=1;i<=n;i++)add(i,i-1);
  for(i=1;i<=n;i++)f[i][i]=a[i],add(i,i);
  for(i=n;i;i--)for(g=i,j=i+1;j<=n;j++){
    while(g<=j&&f[i][g-1]<f[g+1][j])g++;
    f[i][j]=inf;
    T1[j].ask(i,g-1,f[i][j]);
    T2[i].ask(g,j,f[i][j]);
    add(i,j);
  }
  return printf("%d",f[1][n]),0;
}

  

原文地址:https://www.cnblogs.com/clrs97/p/5475108.html