[BZOJ 1044][HAOI2008]木棍分割(二分+Dp)

Description

有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连
接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长
度最大的一段长度最小. 并将结果mod 10007

Solution

二分答案求最小化的最大长度,然后Dp方案数

dp[i][j]表示前i个物品砍j刀保持最大长度不超过ans的方案数

dp[i][j]=∑dp[k][j-1](∑L[l]<=ans k+1<=l<=i)

发现时间空间都要优化

O(n^2m)肯定T了 但是可以知道k是单调递增的 于是用了一个dp[][j-1]的前缀和pre[]随便搞搞,当然也可以单调队列做

空间的话开滚动数组就好了

【然而我滚的时候忘记清零,WAWAWAQAQ,而且只WA一个点,拍了半天没拍出来】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#define Min(a,b) (a<b?a:b)
#define Max(a,b) (a>b?a:b)
#define Mod 10007
using namespace std;
int n,m,L[50005],sum[50005],pre[50005],dp[50005][2];
int Read()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-1;c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';c=getchar();
    }
    return x*f;
}
bool check(int x)
{
    int len=0,cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(len+L[i]>x)cnt++,len=0;
        len+=L[i];
    }
    if(cnt>m)return false;
    return true;
}
int main()
{
    n=Read(),m=Read();
    int l=1,r,ans;
    for(int i=1;i<=n;i++)
    {
        L[i]=Read();
        sum[i]=sum[i-1]+L[i];
        l=Max(l,L[i]);
    }
    r=sum[n];
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    int cur=0,tot=0;
    for(int i=1;i<=n;i++)
    if(sum[i]<=ans)dp[i][0]=1;
    for(int j=1;j<=m;j++)
    {
        cur^=1;
        for(int i=1;i<=n;i++)
        pre[i]=pre[i-1]+dp[i][cur^1];
        for(int i=1;i<=n;i++)
        dp[i][cur^1]=0;
        int k=1;
        for(int i=1;i<=n;i++)
        {
            while(sum[i]-sum[k]>ans&&k<i)
            k++;
            dp[i][cur]+=pre[i-1]-pre[k-1],dp[i][cur]%=Mod;
        }
        tot=(tot+dp[n][cur])%Mod; 
    }
    printf("%d %d\n",ans,tot);
    return 0;
}
posted @ 2017-04-12 19:49  Zars19  阅读(235)  评论(0编辑  收藏  举报