记录编号 294336 评测结果 AAAAAAAAAA
题目名称 [HAOI 2008]木棍分割 最终得分 100
用户昵称 Gravatar浮生随想 是否通过 通过
代码语言 C++ 运行时间 10.778 s
提交时间 2016-08-11 21:32:13 内存使用 3.36 MiB
显示代码纯文本
/*
主要思想:令 f[i][j] 为前 j 根木棍中切 i 刀,(注意ij位置倒过来是为了方便处理)
并且满足最长长度不超过 Len 的方案数,那么:
状态转移方程: f[i][j]=Σf[i-1][k]((1<=k<=j-1)&&(Sum[j]-Sum[k]<=Len));
由于k有条件, (Sum[j]-Sum[k]<=Len)所以对于每一个j值,k均对应一个下界。 
优化的时候,用一个sumf记录一下前j-1个的和,然后用一个while循环找出k的下界。
最后用sumf依次减去k处于下界以下的方案数。
*/ 
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=100010,mod=10007;
int n,m,ans=0;
ll f[2][maxn],a[maxn],sum[maxn];
bool check(int x){
	int ans=0,cnt=0;
	for(int i=1;i<=n;i++){ 
		cnt+=a[i];
		if(cnt+a[i+1]>x){ 
			cnt=0;
			ans++;
		}
	}
	if(ans>m)return 1;
	else return 0; 
}
int main(){
	freopen("stick.in","r",stdin);
	freopen("stick.out","w",stdout);
	ll l=0,r=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		r+=a[i];
		l=max(l,a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid))l=mid+1;
		else r=mid-1;
	}
	printf("%lld ",l);//二分答案,不解释了。 
	for(ll i=1;i<=n;i++){
		if(sum[i]<=l){
			f[0][i]=1;
		}
		else break;
	} 
	for(ll i=1;i<=m;i++){
		int pos=i;ll sumf=0;
		int tem=i%2;
		memset(f[tem],0,sizeof f[tem]);
		for(ll j=i+1;j<=n;j++){//j从i+1开始,由于如果要砍i次,至少要i+1段木棍; 
			sumf+=f[!tem][j-1];//在这里,sumf是记录前j-1个的和; 
			while(sum[j]-sum[pos]>l)
				sumf-=f[!tem][pos],pos++;//处理下界,处于下界一下的方案要减去。 
			f[tem][j]=sumf%mod;
			//注意了!!在这里要取模!!sumf在上面不能取模,因为还有一个减的操作;f[tem][j]却必须取模,不然会越界!! 
		}
		ans+=f[tem][n];ans%=mod;
	}
	printf("%d",ans%mod);
}