BZOJ 1044: [HAOI2008]木棍分割

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1044


分析:第一问直接用二分贪心就行了,不说了。 第二问有dp[i][j]=sum{ dp[k][j-1] | 1<=k<j && sum[i]-sum[k] <= maxlen } 首先注意到,i在往右移的同时,min(k) 只能往右移或者不动,我们只需要维护dp[min(k)][j-1] ~ dp[i-1]j-1]的和就行了。

我的方法比较麻烦。。。经常会有小错误,临界想得我头都大了。。。



代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <string.h>
#include <queue>
#include <vector>
#include <algorithm>
#include <cassert>
#include <set>
#include <map>
#include <cmath>
#include <ctime>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
#define clr(a,x) memset(a,(x),sizeof(a))
#define eps 1e-8
#define LL long long
#define mp make_pair
const int maxn=50000+5;
const int mod=10007;
int L[maxn],n,m;
int f[1005];
int sum[1005];
queue<int> tmp[1005];
//dp[i][j] = sum(dp[k][j-1]); k<j && sum[k+1][j]<=maxsum
int maxlen=0;

void init()
{
    rep(i,0,1005) {
        f[i]=sum[i]=0;
        while(tmp[i].size()) tmp[i].pop();
    }
}

bool ok(int up)
{
    int k=0,S=0;
    rep(i,1,n+1) {
        S += L[i];
        if(S>up) S=L[i],++k;
        if(k>m) return false;
    }
    return true;
}

void up_len()
{
    int l=0,r=0;
    rep(i,1,n+1) r += L[i] , l = max(l,L[i]);
    int m;
    while(l<=r) {
        m=(l+r)>>1;
        if(ok(m)) { maxlen=m; r=m-1; }
        else l=m+1;
    }
}


int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d%d",&n,&m)==2) {
        rep(i,1,n+1) scanf("%d",L+i);
        up_len();
        printf("%d ",maxlen);
        init();
        int h=1,S=L[1];
        sum[0]=1; f[0]=1;
        rep(j,0,m) tmp[j].push(f[j]);
        rep(i,2,n+1) {
            S+=L[i];
            while(S>maxlen) {
                f[0]=0;
                if(S-L[h]<=maxlen) break;
                S -= L[h++];
                rep(j,0,m) {
                    sum[j] -= tmp[j].front();
                    tmp[j].pop();
                    if(sum[j]<0) sum[j]+=mod;
                }
            }
            rrep(j,m,0) {
                if(j-1>=0&&j<=i-1) f[j]=sum[j-1];
                tmp[j].push(f[j]);
                sum[j] += f[j];
                if(sum[j]>=mod) sum[j] -= mod;
            }
        }
        int ans=0;
        rep(i,0,m+1) ans = (ans+f[i])%mod;
        ans = (ans+mod)%mod;
        printf("%d\n",ans);
    }
}

,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值