PKU3017

【原题见 这里】

本沙茶见过的最猥琐的DP题啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊……

设F[i]为将A[1..i]拆分成若干段的最大值最小和,则有
F[i]=min{F[j] + max[j+1, i]}(B[i]<=j<i),其中max[j+1, i]表示A[j+1..i]中的最大值,B[i]表示从i向左最远可以延伸到哪里(也就是满足SUM[x..i]<=m的最小的x值)。B数组可以通过预处理在O(N)时间内得到。
边界:F[0]=0。

下面是优化过程。JZP神犇的论文里面已经够详细了。这里只是简要说明一下。
首先容易证明,F是单调递增的。
然后一个很关键的定理是: 若F[i]的最优决策为j,则有A[j]>∀A[k](j<k<=i)。
证明:用反证法。若A[j+1..i]中存在不小于A[j]的值,则可得max[j..i]=max[j+1..i],又因为F单调递增,所以F[j-1]+max[j..i]<=F[j]+max[j+1.i],即决策(j-1)一定不比决策j差,也就是决策j不可能成为最优决策。
这样,可以维护一个下标严格递增、A值严格递减的队列Q(即对于队列中的任意两个元素Q[i]和Q[j],若i<j,则Q[i].pos<Q[j].pos且A[Q[i].pos]>A[Q[j].pos],具体实现时pos可省略)。则可能成为最优决策的决策要么是在这个队列Q里,要么是B[i]。对于队列中的某个决策Q[x],该决策导出的值为F[Q[x]]+A[Q[x + 1]](很容易证明max[Q[x]+1..i]=A[Q[x + 1]]),找到这些导出的值中的最小值即可(注意,队尾元素没有导出值)。对于决策B[i],只需要在预处理的时候同时得到MAX[i]=max[B[i]+1..i]即可(也可以在O(N)时间内得到),决策B[i]导出的值为F[B[i]]+MAX[i]。
在删除队首过时元素的时候,需要把导出值也删除,删除队尾元素也一样,插入的时候,若插入前队列不为空,则需要插入一个导出值。也就是,需要一个支持在对数时间内进行插入、删除任意结点、找最小值等操作,显然用平衡树最好。

注意事项:
(1)不管是在队首删除还是在队尾删除,若删除的是队列中的最后一个元素,则不需要在平衡树中删除导出值;
(2)插入时,若插入前队列为空,则不需要在平衡树中插入导出值;
(3)在计算F[i]时,应先将决策i压入。

代码:
#include  < iostream >
#include 
< stdio.h >
using   namespace  std;
#define  re1(i, n) for (int i=1; i<=n; i++)
const   int  MAXN  =   100001 ;
struct  node {
    
int  l, r, p, sz0, sz, mul;
    
long   long  v;
} T[MAXN];
const   long   long  INF  =   ~ 0Ull  >>   2 ;
int  n, N  =   0 , a[MAXN], b[MAXN], MAX[MAXN], Q[MAXN], front  =   0 , rear  =   - 1 , root  =   0 ;
long   long  m, F[MAXN], res  =   0 ;
void  init()
{
    cin 
>>  n  >>  m;
    re1(i, n) scanf(
" %d " & a[i]); a[ 0 =   ~ 0U   >>   2 ;
}
void  prepare()
{
    re1(i, n) 
if  (a[i]  >  m) {res  =   - 1 return ;}
    
int  x  =   1 ;
    
long   long  sum  =   0 ;
    re1(i, n) {
        
for  (sum += a[i]; sum > m; sum -= a[x ++ ]) ;
        b[i] 
=  x  -   1 ;
    }
    re1(i, n) {
        
for  (; front <= rear  &&  Q[front] <= b[i]; front ++ ) ;
        
for  (; front <= rear  &&  a[Q[rear]] <= a[i]; rear -- ) ;
        Q[
++ rear]  =  i; MAX[i]  =  a[Q[front]];
    }
}
void  vst( int  x)
{
    
if  (x) {
        cout 
<<  T[x].v  <<   '   ' ;
        vst(T[x].l); vst(T[x].r);
    }
}
void  slc( int  _p,  int  _c)
{
    T[_p].l 
=  _c; T[_c].p  =  _p;
}
void  src( int  _p,  int  _c)
{
    T[_p].r 
=  _c; T[_c].p  =  _p;
}
void  upd( int  x)
{
    T[x].sz0 
=  T[T[x].l].sz0  +  T[T[x].r].sz0  +  T[x].mul;
    T[x].sz 
=  T[T[x].l].sz  +  T[T[x].r].sz  +   1 ;
}
void  lrot( int  x)
{
    
int  y  =  T[x].p;
    
if  (y  ==  root) T[root  =  x].p  =   0 else  { int  p  =  T[y].p;  if  (y  ==  T[p].l) slc(p, x);  else  src(p, x);}
    src(y, T[x].l); slc(x, y); T[x].sz0 
=  T[y].sz0; T[x].sz  =  T[y].sz; upd(y);
}
void  rrot( int  x)
{
    
int  y  =  T[x].p;
    
if  (y  ==  root) T[root  =  x].p  =   0 else  { int  p  =  T[y].p;  if  (y  ==  T[p].l) slc(p, x);  else  src(p, x);}
    slc(y, T[x].r); src(x, y); T[x].sz0 
=  T[y].sz0; T[x].sz  =  T[y].sz; upd(y);
}
void  maintain( int  x,  bool  ff)
{
    
int  z;
    
if  (ff) {
        
if  (T[T[T[x].r].r].sz  >  T[T[x].l].sz) {z  =  T[x].r; lrot(z);}
        
else   if  (T[T[T[x].r].l].sz  >  T[T[x].l].sz) {z  =  T[T[x].r].l; rrot(z); lrot(z);}  else   return ;
    } 
else  {
        
if  (T[T[T[x].l].l].sz  >  T[T[x].r].sz) {z  =  T[x].l; rrot(z);}
        
else   if  (T[T[T[x].l].r].sz  >  T[T[x].r].sz) {z  =  T[T[x].l].r; lrot(z); rrot(z);}  else   return ;
    }
    maintain(T[z].l, 
0 ); maintain(T[z].r,  1 ); maintain(z,  0 ); maintain(z,  1 );
}
int  find( long   long  _v)
{
    
int  i  =  root;
    
long   long  v0;
    
while  (i) {
        v0 
=  T[i].v;
        
if  (_v  ==  v0)  return  i;  else   if  (_v  <  v0) i  =  T[i].l;  else  i  =  T[i].r;
    }
    
return   0 ;
}
int  Find_Kth( int  K)
{
    
int  i  =  root, s0, m0;
    
while  ( 1 ) {
        s0 
=  T[T[i].l].sz0; m0  =  T[i].mul;
        
if  (K  <=  s0) i  =  T[i].l;  else   if  (K  <=  s0  +  m0)  return  i;  else  {K  -=  s0  +  m0; i  =  T[i].r;}
    }
}
void  ins( long   long  _v)
{
    
if  ( ! root) {
        T[
++ N].v  =  _v; T[N].l  =  T[N].r  =  T[N].p  =   0 ; T[N].sz0  =  T[N].sz  =  T[N].mul  =   1 ; root  =  N;
    } 
else  {
        
int  i  =  root, j;
        
long   long  v0;
        
while  ( 1 ) {
            T[i].sz0
++ ; v0  =  T[i].v;
            
if  (_v  ==  v0) {T[i].mul ++ return ;}  else   if  (_v  <  v0) j  =  T[i].l;  else  j  =  T[i].r;
            
if  (j) i  =  j;  else   break ;
        }
        T[
++ N].v  =  _v; T[N].l  =  T[N].r  =   0 ; T[N].sz0  =  T[N].sz  =  T[N].mul  =   1 if  (_v  <  v0) slc(i, N);  else  src(i, N);
        
while  (i) {T[i].sz ++ ; maintain(i, _v  >  T[i].v); i  =  T[i].p;}
    }
}
void  del( int  x)
{
    
if  (T[x].mul  >   1 ) {
        T[x].mul
-- while  (x) {T[x].sz0 -- ; x  =  T[x].p;}
    } 
else  {
        
int  l  =  T[x].l, r  =  T[x].r, p;
        
if  (l  &&  r) {
            
int  y;  while  (y  =  T[l].r) l  =  y;
            T[x].v 
=  T[l].v; T[x].mul  =  T[l].mul; p  =  T[l].p;
            
if  (l  ==  T[p].l) slc(p, T[l].l);  else  src(p, T[l].l);
            
while  (p) {upd(p); p  =  T[p].p;}
        } 
else  {
            
if  (x  ==  root) T[root  =  l  +  r].p  =   0 else  {p  =  T[x].p;  if  (x  ==  T[p].l) slc(p, l  +  r);  else  src(p, l  +  r);  while (p) {upd(p); p  =  T[p].p;}}
        }
    }
}
long   long  h( int  x)
{
    
return  F[Q[x]]  +  a[Q[x  +   1 ]];
}
void  solve()
{
    F[
0 =   0 ; front  =   0 ; rear  =   0 ; Q[ 0 =   0 ;
    re1(i, n) {
        
for  (; front <= rear  &&  Q[front] < b[i];) { if  (front  <  rear) del(find(h(front))); front ++ ;}
        
for  (; front <= rear  &&  a[Q[rear]] <= a[i];) { if  (front  <  rear) del(find(h(rear  -   1 ))); rear -- ;}
        Q[
++ rear]  =  i;  if  (front  <  rear) ins(h(rear  -   1 ));
        
if  (root) F[i]  =  T[Find_Kth( 1 )].v;  else  F[i]  =  INF;
        
if  (F[b[i]]  +  MAX[i]  <  F[i]) F[i]  =  F[b[i]]  +  MAX[i];
    }
    res 
=  F[n];
}
void  pri()
{
    cout 
<<  res  <<  endl;
}
int  main()
{
    init();
    prepare();
    
if  ( ! res) solve();
    pri();
    
return   0 ;
}

你可能感兴趣的:(PKU3017)