[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

Solution

第一问二分+贪心,第二问直接前缀和优化dp就好了,dp要滚动一维。

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 2e5+10;
const int mod = 10007;

int n,m,a[maxn],q[maxn],f[2][maxn],ans,sum[maxn],r[maxn],w[maxn];

int check(int x) {
	int res=0,tmp=0;
	for(int i=1;i<=n;i++) {
		res+=a[i];
		if(a[i]>x) return 0;
		if(res>x) res=a[i],tmp++;
	}return tmp<=m;
}

void solve1() {
	int l=0,R=sum[n],mid;
	while(l<=R) if(check(mid=((l+R)>>1))) R=mid-1,ans=mid;else l=mid+1;
	printf("%d ",ans);
}

void solve2() {
	for(int i=1;i<=n;i++) if(sum[i]<=ans) f[0][i]=1;else break;
	for(int i=1,lst=0;i<=n;i++) {while(sum[i]-sum[lst]>ans) lst++;r[i]=lst;}
	int res=f[0][n];
	for(int t=1;t<=m;t++) {
		int i=t&1;memset(f[i],0,(n+4)*4);
		for(int j=1;j<=n;j++) w[j]=(w[j-1]+f[i^1][j])%mod;
		for(int j=1;j<=n;j++) f[i][j]=(w[j-1]-w[max(r[j]-1,0)])%mod;
		res=(res+f[i][n])%mod;
	}write((res+mod)%mod);
}

int main() {
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]),sum[i]=sum[i-1]+a[i];
	solve1();solve2();
	return 0;
}
posted @ 2019-02-15 11:23  Hyscere  阅读(62)  评论(0编辑  收藏