# BZOJ 1044 木棍分割（二分答案 + DP优化）

## 1044: [HAOI2008]木棍分割

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3830  Solved: 1453
[Submit][Status][Discuss]

## Description

有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连

## Input

输入文件第一行有2个数n,m.接下来n行每行一个正整数Li,表示第i根木棍的长度.n<=50000,0<=m<=min(n-1,10
00),1<=Li<=1000.

## Output

输出有2个数, 第一个数是总长度最大的一段的长度最小值, 第二个数是有多少种砍的方法使得满足条件.

3 2
1
1
10

10 2

## Source

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

typedef long long LL;

const int N   = 50010;
const int mod = 10007;

int n, m;
int a[N], s[N], f[N][2];
int l, r, ans;
int len, cnt;
int k, now;

inline bool check(int x){
int now = a[1], ret = 0;
rep(i, 2, n){
if (now + a[i] <= x){
now += a[i];
}

else{
now = a[i];
++ret;
}
}

return ret <= m;
}

int main(){

scanf("%d%d", &n, &m);
rep(i, 1, n){
scanf("%d", a + i);
l = max(l, a[i]);
r += a[i];
s[i] = s[i - 1] + a[i];
}

l = 0, r = 1e8;

while (l + 1 < r){
int mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}

if (check(l)) len = l; else len = r;

rep(i, 0, m){
cnt = 0;
k = 1;
rep(j, 1, n){
if (i == 0){
if (s[j] <= len) f[j][now] = 1;
else f[j][now] = 0;
}

else{
while (k < j && s[j] - s[k] > len){
cnt -= f[k][now ^ 1];
cnt = (cnt + mod) % mod;
++k;
}
f[j][now] = cnt;
}

cnt = (cnt + f[j][now ^ 1]) % mod;
}

(ans += f[n][now]) %= mod;
now ^= 1;
}

printf("%d %d\n", len, ans);
return 0;
}


posted @ 2017-07-31 10:47  cxhscst2  阅读(269)  评论(0编辑  收藏