P2511 [HAOI2008]木棍分割(二分&前缀和优化dp)

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2511 [HAOI2008]木棍分割(二分&前缀和优化dp)相关的知识,希望对你有一定的参考价值。

P2511 [HAOI2008]木棍分割(二分&前缀和优化dp)

最大值最小,二分的板子。

找到最值后,就是 d p dp dp求方案数。

考虑朴素的 d p ( i , j ) dp(i,j) dp(i,j) i i i个木棍分成 j j j组的方案数。

有: d p ( i , j ) = ∑ k = l e f t [ i ] i − 1 d p ( k , j − 1 ) , s u m ( k + 1 , i ) ≤ l e n dp(i,j)=\\sum\\limits_{k=left[i]}^{i-1}dp(k,j-1),sum(k+1,i)\\le len dp(i,j)=k=left[i]i1dp(k,j1),sum(k+1,i)len

因为前缀和的单调性,显然转移的求和是可以预处理的。考虑前缀和优化 d p dp dp

d p ( i , j ) = p r e ( i , j − 1 ) − p r e ( k , j − 1 ) dp(i,j)=pre(i,j-1)-pre(k,j-1) dp(i,j)=pre(i,j1)pre(k,j1)

p r e ( i , j ) = ∑ k = 1 i d p ( k , j ) pre(i,j)=\\sum\\limits_{k=1}^i dp(k,j) pre(i,j)=k=1idp(k,j)

每次找 k k k很麻烦,所以考虑预处理 l e f t [ i ] = k left[i]=k left[i]=k,因为前缀和具有单调性,所以直接可以线性处理出来。

因为 d p , p r e dp,pre dp,pre转移都只需用到前一维,考虑滚动数组优化空间。

d p i = p r e i − p r e l e f t [ i ] dp_i=pre_i-pre_{left[i]} dpi=preipreleft[i]

p r e j = ∑ k = 1 i d p k pre_j=\\sum\\limits_{k=1}^i dp_k prej=k=1idpk

时间复杂度: O ( n l o g n + n m ) O(nlogn+nm) O(nlogn+nm)

#include<bits/stdc++.h>
#define ll int
using namespace std;
inline void read(ll&x){
    x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
}
const ll ha=10007;
ll n,m,l,r,mid,ans;
ll a[50005],s[50005],f[50005],tmp=0;
ll lef[50005],now;
inline bool work(ll x){
    ll k=0,t=1;
    for(ll i=1;i<=n;i++)if(a[i]>x) return false;
    else if(k+a[i]>x){
        t++,k=a[i];
        if(t>m) return false;
    }    else k+=a[i];
    return true;
}
int main(){
    read(n),read(m);
    m++;
    for(ll i=1;i<=n;i++) read(a[i]);
    l=1,r=50000000;
    while(l<=r){
        mid=l+r>>1;
        if(work(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<ans<<' ';
    now=0;
    for(ll i=1;i<=n;i++){
      a[i]+=a[i-1];
      while(a[i]-a[now]>ans) now++;
      lef[i]=now-1;
    }
    memset(f,0,sizeof(f));
//    f[0]=1;
    fill(s,s+n+1,1);
    for(ll i=1;i<=m;i++){
        for(ll j=1;j<=n;j++) f[j]=(s[j-1]-s[lef[j]])%ha;
        s[0]=0;
        for(ll j=1;j<=n;j++) s[j]=f[j]+s[j-1];
        tmp=(tmp+f[n])%ha;
    }
    cout<<tmp;
    return 0;
}

以上是关于P2511 [HAOI2008]木棍分割(二分&前缀和优化dp)的主要内容,如果未能解决你的问题,请参考以下文章

P2511 [HAOI2008]木棍分割

bzoj 1044 [HAOI2008]木棍分割(二分+贪心,DP+优化)

BZOJ1044: [HAOI2008]木棍分割 二分+区间DP

BZOJ1044[HAOI2008]木棍分割 二分+DP

[BZOJ1044][HAOI2008]木棍分割 二分+贪心+dp+前缀和优化

BZOJ1044: [HAOI2008]木棍分割