BZOJ1044: [HAOI2008]木棍分割(洛谷P2511)

292 篇文章 1 订阅
281 篇文章 1 订阅

二分 DP

BZOJ题目传送门
洛谷题目传送门

第一问二分后贪心即可。第二问要DP。

f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个分成 j j j段的方案数。那么有 f [ i ] [ j ] = ∑ f [ k ] [ j − 1 ] ( ∑ p = k p &lt; i a [ p ] ≤ a n s 1 ) f[i][j]=\sum f[k][j-1](\sum_{p=k}^{p&lt;i} a[p]\leq ans1) f[i][j]=f[k][j1](p=kp<ia[p]ans1),其中 a n s 1 ans1 ans1为第一问答案.

首先这个东西只和 j j j的前一维有关,那么可以滚掉或直接砍掉一维。而 i i i这一维又是一个前缀和的性质,且 k k k是单调往右的。于是可以预处理 l [ i ] = k i l[i]=k_i l[i]=ki或者每次推一遍。设 S [ i ] = ∑ f [ i ] S[i]=\sum f[i] S[i]=f[i],那么 f [ i ] = S [ i − 1 ] − S [ l [ i ] − 1 ] f[i]=S[i-1]-S[l[i]-1] f[i]=S[i1]S[l[i]1]

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50005
#define F inline
using namespace std;
const int p=10007;
int n,m,ans1,ans2,f[N],g[N],s[N],a[N],l[N];
F char readc(){
	static char buf[100000],*l=buf,*r=buf;
	if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
	return l==r?EOF:*l++;
}
F int _read(){
	int x=0; char ch=readc();
	while (!isdigit(ch)) ch=readc();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
	return x;
}
F bool pd(int p){
	int s=0,S=0;
	for (int i=1;i<=n;i++) S+a[i]>p?s++,S=a[i]:S+=a[i];
	return s<=m;
}
F void dp(){
	for (int now=0,i=1;i<=n;i++){
		while (s[i]-s[now]>ans1) now++; l[i]=now;
	}
	for (int i=1;i<=n&&s[i]<=ans1;i++) f[i]=1;
	for (int j=1;j<=m;j++){
		for (int i=1;i<=n;i++) g[i]=(g[i-1]+f[i])%p;
		for (int i=1;i<=n;i++) f[i]=(g[i-1]-g[l[i]-1]+p)%p;
		(ans2+=f[n])%=p;
	}
}
int main(){
	n=_read(),m=_read(); int l=0,r,mid;
	for (int i=1;i<=n;i++) l=max(l,a[i]=_read()),s[i]=s[i-1]+a[i];
	for (r=s[n];l<=r;) pd(mid=l+r>>1)?ans1=mid,r=mid-1:l=mid+1;
	return dp(),printf("%d %d\n",ans1,ans2),0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值