# BZOJ1044 [HAOI2008]木棍分割 【二分+Dp】

## 1044: [HAOI2008]木棍分割

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 4281  Solved: 1644
[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

## HINT

【注意：取模过程中涉及减法，最后答案输出时要取回正数】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
using namespace std;
const int maxn = 50005,maxm = 1005,INF = 1000000000,P = 10007;
inline int RD(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
return out * flag;
}
int n,m,f[2][maxn],A[maxn],sum[maxn],S[maxn],mx = 0,Sum = 0,ans = 0;
bool check(int M){
if (mx > M) return false;
int cnt = 0,tot = 0;
for (int i = 1; i <= n; i++){
if (tot + A[i] > M) cnt++,tot = 0;
tot += A[i];
if (cnt > m) return false;
}
return true;
}
int main(){
n = RD(); m = RD();
REP(i,n) A[i] = RD(),sum[i] = sum[i - 1] + A[i],mx = max(mx,A[i]);
int l = 1,r = sum[n],mid;
while (l < r){
mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout<<(mx = l)<<' ';
f[0][0] = 1;
for (int k = 0,p = 0; k <= m; k++){
S[0] = f[p][0];
REP(i,n) S[i] = (S[i - 1] + f[p][i]) % P;
p ^= 1; int pos = 0;
f[p][0] = 0;
for (int i = 1; i <= n; i++){
/*
for (int j = i - 1; j > 0; j--)
if (sum[i] - sum[j] <= mx)
f[p][i] = (f[p][i] + f[p ^ 1][j]) % P;
else break;*/
while (sum[i] - sum[pos] > mx) pos++;
f[p][i] = (S[i - 1] - (pos ? S[pos - 1] : 0)) % P;
}
ans = (ans + f[p][n]) % P;
}
cout<<(ans + P) % P<<endl;
return 0;
}


posted @ 2017-12-08 18:45  Mychael  阅读(112)  评论(0编辑  收藏