首页 > 代码库 > [haoi2008]木棍分割

[haoi2008]木棍分割

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

 

第一问:明显的二分答案;

第二问:状态转移方程很容易搞出来:f[i][j]=Σf[k][j-1]   sum[i]-sum[k]<=ans1 

看起来是个O(n2m)的dp,但实际上,k的取值只可能是i之前连续的一段,用个q[j-1]表示计算f[i][j]时前面的合法的f[k][j-1]的和,然后类似单调队列的搞一搞即可;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<algorithm>
#include<cstdlib>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cmath>
using namespace std;
#define FILE "1"
#define LL long long 
#define up(i,j,n) for(int i=j;i<=n;i++)
#define pii pair<int,int>
#define piii pair<int,pair<int,int> >
template<typename T> inline bool chkmin(T &a,T b){return a>b?a=b,true:false;}
template<typename T> inline bool chkmax(T &a,T b){return a<b?a=b,true:false;}
namespace IO{
    char *fs,*ft,buf[1<<15];
    inline char gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
    inline int read(){
        int x=0,ch=gc();bool f=0;
        while(ch<0||ch>9){if(ch==-)f=1;ch=gc();}
        while(ch<=9&&ch>=0){x=(x<<1)+(x<<3)+ch-0;ch=gc();}
        return f?-x:x;
    }
}using namespace IO;
namespace OI{
    const int maxn(51000),inf(100000000),mod(10007);
    int n,m;
    int a[maxn],sum[maxn];
    bool check(int mid){
        int sum=0,ans=1;
        for(int i=1;i<=n;i++){
            sum+=a[i];
            if(sum>mid)ans++,sum=a[i];
        }
        if(ans>m)return 0;
        else return 1;
    }
    int q[maxn],head;
    short int f[maxn][1010];
    void slove(){
        n=read(),m=read();m++;
        int maxx=0;
        up(i,1,n)a[i]=read(),chkmax(maxx,a[i]);
        int left=maxx,right=inf,mid,ans;
        while(left<=right){
            mid=(left+right)>>1;
            if(check(mid))right=mid-1,ans=mid;
            else left=mid+1;
        }
        up(i,1,n)sum[i]=sum[i-1]+a[i];
        head=0;
        f[0][0]=1;
        q[0]=1;
        for(int i=1;i<=n;i++){
            while(sum[i]-sum[head]>ans){
                for(int j=1;j<=m&&j<=i;j++)q[j-1]=(q[j-1]-f[head][j-1]+mod)%mod;
                head++;
            }
            for(int j=1;j<=m&&j<=i;j++)f[i][j]=q[j-1];
            for(int j=min(i,m);j>=1;j--)q[j]=(q[j]+q[j-1])%mod;
        }
        int y=0;
        for(int i=1;i<=m;i++)y=(y+f[n][i])%mod;
        printf("%d %d\n",ans,y);
    }
}
int main(){
    using namespace OI;
    slove();
    return 0;
}

 

[haoi2008]木棍分割