【BZOJ1044】【HAOI2008】木棍分割

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,1000),1<=Li<=1000.

Output

  輸出有2個數, 第一個數是總長度最大的一段的長度最小值, 第二個數是有多少種砍的方法使得滿足條件.

Sample Input

3 2
1
1
10

Sample Output

10 2

HINT

兩種砍的方法: (1)(1)(10)和(1 1)(10)

題解

很好的一道二分+優化dp的題.

首先看到最大的最小就想到二分,每次切儘量長的塊,看最後要切幾刀。二分完了就要dp了,他讓求最多切m塊的最優解,所以問什麼設什麼,dp[i][j]表示前i塊切j刀的最優解,所以dp[i][j]=sigma dp[k][j-1](sum[i]-sum[k]<=len).爲什麼這麼設就可以了呢?因爲二分時你已經算出了最小的長度了,不能再小了,所以只要滿足這個條件,分割的木棍中的最大長度一定是len這麼長。還沒完這麼做時間複雜度爲O(n*n*m)的足夠超時,空間複雜度也不行。所以要進行優化,發現j只與j-1有關,所以可以用滾動數組把這一維優化掉,由於i是單調遞增的所以k也是單調遞增的(即最前面的sum[i]-sum[k]<=len的k的位置是單調的),所以就可以用一個前綴和的思想,算出一個狀態最前面的k是什麼,這個可以預處理出來,這樣就可以把這個問題解決了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=50010;
const int mod = 10007;
int sum[maxn],a[maxn],f[maxn][3],g[maxn][3],pos[maxn],maxx;
int n,m;
inline bool pan(int x){
	int ans=0,last=0;;
	if(maxx>x) return false;
	for(int i=1;i<=n;i++){
		if(sum[i]-last>x) {
			ans++;
			last=sum[i-1]; 
		}
		if(ans>m) return false;
	}
	return true;
}
inline int binary(){
	int l=0,r=sum[n];
	while(l<=r){
		int mid=(l+r)>>1;
		if(pan(mid)) r=mid-1;
		else l=mid+1; 
	}
	if(pan(l)) return l;
	else return r; 
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
	    scanf("%d",&a[i]);
	    maxx=max(maxx,a[i]);
	    sum[i]=sum[i-1]+a[i];
	}
    int len=binary(),ans=0;
	
	for (int i=1;i<=n;i++)
    if (sum[i]<=len) f[i][0]=1;
    else break;
	for(int i=1;i<=n;i++){
		if(sum[i]<=len) continue;
		for(int j=i-1;j>=0;j--)
		 	if(sum[i]-sum[j]>len){
		 		pos[i]=j+1;
				break; 
			}
	}
	int x=0;
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			g[j][x]=g[j-1][x]+f[j][x];
		}
		x^=1;
		for(int j=1;j<=n;j++)
		f[j][x]=(g[j-1][x^1]-g[max(pos[j]-1,0)][x^1])%mod;
		
		(ans+=f[n][x])%=mod;
	}

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

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