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);
}





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