BZOJ1044

1044: [HAOI2008]木棍分割

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3291  Solved: 1238
[Submit][Status][Discuss]

Description

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

Input

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

Output

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

Sample Input

3 2
1
1
10

Sample Output

10 2

HINT

两种砍的方法: (1)(1)(10)和(1 1)(10)


相信O(nm)...

前缀和优化DP,用单调队列维护.


#pragma GCC optimize(2)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxm 1010
#define maxn 50100
#define mod 10007
using namespace std;
int dp[maxn],q[maxn],pos[maxn],ans;
int a[maxn],s[maxn],ql,qr,sum[maxn],p[maxn],n,m,l,r;
bool check(int ans){
	int p=0,len=0;
	for(int i=1;i<=n;++i){
		if(len+a[i]>ans)p++,len=a[i];
		else len+=a[i];
	}
	return p<=m;
}
int inline update(int x){
	return x>mod?x-mod:x;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		s[i]=a[i]+s[i-1];
		l=max(l,a[i]),r+=a[i];
	}
	while(l<r){
		int mid=l+r>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	printf("%d ",l);
	for(int i=1;i<=n;++i)sum[i]=1;
	memset(p,-1,sizeof(p));
	ql=qr=0,q[qr++]=0;
	for(int j=1;j<=n;++j){
		while(ql<qr&&s[j-1]-s[q[ql]]>l)ql++;
		if(ql<qr)p[j]=q[ql];
		q[qr++]=j;
	}
	for(int i=1;i<=m;++i){
		for(int j=1;j<=n;++j)
			if(p[j]!=-1)dp[j]=update(sum[j-1]-sum[p[j]]+mod);
		for(int j=1;j<=n;++j)sum[j]=update(sum[j-1]+dp[j]);
		for(int j=n;j>=1&&s[n]-s[j-1]<=l;--j)ans=update(ans+dp[j]);
	}
	printf("%d",(ans+mod)%mod);
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章