BZOJ 1044: [HAOI2008]木棍分割

Description

求 \(n\) 根木棍长度为 \(L\) ,分成 \(m\) 份,使最长长度最短,并求出方案数.

Sol

二分+DP.

二分很简单啊,然后就是方案数的求法.

状态就是 \(f[i][j]\) 表示前 \(i\) 根木棍,分成 \(j\) 份的方案数.

然后就是转移,我们发现这个转移是 \(O(n)\) 的,但他是一个前缀和的形式.

因为决策点单调,我们可以用尺取法求出最远范围,然后用前缀和搞一下就可以了.

空间开不下可以滚qwq.

Code

/**************************************************************
    Problem: 1044
    User: BeiYu
    Language: C++
    Result: Accepted
    Time:4912 ms
    Memory:2264 kb
****************************************************************/
 
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
 
const int N = 50005;
const int M = 1005;
const int p = 10007;
 
int n,m,l,r,t,mid,lim,ans;
int a[N],b[N],c[N];
int f[2][N];
 
inline int in(int x=0,char ch=getchar()){ while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x; }
int check(int x){
    int nd=0,tmp=0;
    for(int i=1;i<=n;i++) if(tmp+a[i]>x) tmp=a[i],nd++;else tmp+=a[i];
    if(tmp) nd++;
    return nd<=m;
}
int main(){
     
    n=in(),m=in()+1;
    for(int i=1;i<=n;i++) a[i]=in(),b[i]=b[i-1]+a[i],r+=a[i],l=max(l,a[i]);
     
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid)) r=mid-1;
        else l=mid+1;
    }cout<<l<<" ";
     
    lim=l,l=1,t=0;
     
    for(int i=1;i<=n;i++){
        t+=a[i];
        while(t>lim) t-=a[l++];
        c[i]=l;
    }
     
//  for(int i=1;i<=n;i++) cout<<c[i]<<" ";cout<<endl;
     
    int cur=1;
    for(int i=0;i<=n;i++) f[0][i]=1;
    for(int i=1;i<=m;i++){
        memset(f[cur],0,sizeof(f[cur]));
        for(int j=1;j<=n;j++){
            f[cur][j]=((f[cur][j-1]+f[cur^1][j-1]-f[cur^1][c[j]-2])%p+p)%p;
        }ans=((ans+f[cur][n]-f[cur][n-1])%p+p)%p,cur^=1;
    }cout<<ans<<endl;
    return 0;
}

  

posted @ 2016-10-09 20:52  北北北北屿  阅读(217)  评论(0编辑  收藏  举报