[bzoj1044][HAOI2008][木棍分割] (二分+贪心+dp+队列优化)
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
Sample Output
HINT
两种砍的方法: (1)(1)(10)和(1 1)(10)
Solution
from Joe Fan,一个写得很好的题解
第一问是一个十分显然的二分,贪心Check(),很容易就能求出最小的最大长度 Len 。
第二问求方案总数,使用 DP 求解。
使用前缀和,令 Sum[i] 为前 i 根木棍的长度和。
令 f[i][j] 为前 i 根木棍中切 j 刀,并且满足最长长度不超过 j 的方案数,那么:
状态转移方程: f[i][j] = Σ f[k][j-1] ((1 <= k <= i-1) && (Sum[i] - Sum[k] <= Len))
这样的空间复杂度为 O(nm) ,时间复杂度为 O(n^2 m) 。显然都超出了限制。
下面我们考虑 DP 的优化。
1) 对于空间的优化。
这个比较显然,由于当前的 f[][j] 只与 f[][j-1] 有关,所以可以用滚动数组来实现。
f[i][Now] 代替了 f[i][j] , f[i][Now^1] 代替了 f[i][j-1] 。为了方便,我们把 f[][Now^1] 叫做 f[][Last] 。
这样空间复杂度为 O(n) 。满足空间限制。
2) 对于时间的优化。
考虑优化状态转移的过程。
对于 f[i][Now] ,其实是 f[mink][Last]...f[i-1][Last] 这一段 f[k][Last] 的和,mink 是满足 Sum[i] - Sum[k] <= Len 的最小的 k ,那么,对于从 1 到 n 枚举的 i ,相对应的 mink 也一定是非递减的(因为 Sum[i] 是递增的)。我们记录下 f[1][Last]...f[i-1][Last] 的和 Sumf ,mink 初始设为 1,每次对于 i 将 mink 向后推移,推移的同时将被舍弃的 p 对应的 f[p][Last] 从 Sumf 中减去。那么 f[i][Now] 就是 Sumf 的值。
这样时间复杂度为 O(nm) 。满足时间限制。
//KaibaSeto 20170128
#include <stdio.h>
#include <memory.h>
#define MaxN 50010
#define MaxBuf 1<<22
#define mo 10007
#define RG register
#define mid ((x>>1)+(y>>1)+(x&y&1))
char B[MaxBuf],*p=B;
template<class Type>inline void Rin(RG Type &x){
x=;
while(*p<''||*p>'')p++;
while(*p>=''&&*p<='')
x=(x<<)+(x<<)+*p++-'';
}
int n,m,l[MaxN],s[MaxN],ans,cnt,f[][MaxN];
inline bool Jud(RG int lim){
RG int tot=,block_num=;
for(RG int i=;i<=n;i++){
if(l[i]>lim)return false;
tot+=l[i];
if(tot>lim){
block_num++; tot=l[i];
}
if(block_num>m)return false;
}
return true;
}
inline void bin_search(){
RG int x=,y=s[n];
while(x<=y)
Jud(mid)?(ans=mid,y=mid-):x=mid+;
printf("%d ",ans);
}
inline void approach(){
RG int c=;
for(RG int i=;i<=m;i++){
c^=;
RG int tot=,k=;
for(RG int j=;j<=n;j++){
if(!i)f[c][j]=(s[j]<=ans);
else{
while(k<j&&s[j]-s[k]>ans){
tot-=f[c^][k];
(tot+=mo)%=mo;
++k;
}
f[c][j]=tot;
}
(tot+=f[c^][j])%=mo;
}
(cnt+=f[c][n])%=mo;
}
printf("%d\n",cnt);
}
#define FO(x) {freopen(#x".in","r",stdin);}
int main(){
FO(bzoj1044);
fread(p,,MaxBuf,stdin);
Rin(n),Rin(m);
for(RG int i=;i<=n;i++){
Rin(l[i]);
s[i]=s[i-]+l[i];
}
bin_search();
approach();
return ;
}
[bzoj1044][HAOI2008][木棍分割] (二分+贪心+dp+队列优化)的更多相关文章
- [BZOJ1044][HAOI2008]木棍分割 二分+贪心+dp+前缀和优化
1044: [HAOI2008]木棍分割 Time Limit: 10 Sec Memory Limit: 162 MB Submit: 4112 Solved: 1577 [Submit][St ...
- BZOJ 1044: [HAOI2008]木棍分割(二分答案 + dp)
第一问可以二分答案,然后贪心来判断. 第二问dp, dp[i][j] = sigma(dp[k][j - 1]) (1 <= k <i, sum[i] - sum[k] <= ans ...
- [BZOJ1044][HAOI2008]木棍分割 二分 + 单调队列优化dp + 滚动数组优化dp
Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长 ...
- bzoj1044: [HAOI2008]木棍分割 二分+dp
有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少 ...
- 【BZOJ】1044: [HAOI2008]木棍分割 二分+区间DP
链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1044 Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, ...
- bzoj1044[HAOI2008]木棍分割 单调队列优化dp
1044: [HAOI2008]木棍分割 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4314 Solved: 1664[Submit][Stat ...
- BZOJ1044 [HAOI2008]木棍分割 【二分+Dp】
1044: [HAOI2008]木棍分割 Time Limit: 10 Sec Memory Limit: 162 MB Submit: 4281 Solved: 1644 [Submit][St ...
- BZOJ1044: [HAOI2008]木棍分割
1044: [HAOI2008]木棍分割 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1580 Solved: 567[Submit][Statu ...
- BZOJ1044: [HAOI2008]木棍分割(dp 单调队列)
题意 题目链接 Sol 比较套路的一个题. 第一问二分答案check一下 第二问设\(f[i][j]\)表示前\(i\)个数,切了\(j\)段的方案数,单调队列优化一下. 转移的时候只需要保证当前段的 ...
随机推荐
- RDA 多屏参流程
一.RDA MAKEFILE的本地变量 在介绍多屏参之前,先看一下./code/env.conf的包含过程,通过./code/Makefile.project加载,env.conf中所有的变量,都变为 ...
- linux下的C语言开发 进程创建 延伸的几个例子
在Linux下面,创建进程是一件十分有意思的事情.我们都知道,进程是操作系统下面享有资源的基本单位.那么,在linux下面应该怎么创建进程呢?其实非常简单,一个fork函数就可以搞定了.但是,我们需要 ...
- linux下的C语言开发 GDB的例子
在很多人的眼里,C语言和linux常常是分不开的.这其中的原因很多,其中最重要的一部分我认为是linux本身就是C语言的杰出作品.当然,linux操作系统本身对C语言的支持也是相当到位的.作为一个真正 ...
- NOIP 2011 Mayan游戏 大暴搜
题目链接:https://www.luogu.org/problemnew/show/P1312 我的第一篇题解!! 当然感谢ZAGER 的提示,他的链接https://www.cnblogs.com ...
- Git-flow 一个简单高效的Git工作流
背景 由于Git的分支比SVN更好管理且更易使用,最近团队从SVN迁移到Git,需要重新规划开发流程,最终确定使用Git-flow工作流,这是目前比较流行的一种分支模型,下面是Git-flow的简易流 ...
- 搞笑代码注释,佛祖保佑 永无BUG
佛祖保佑 永无BUG 上传图片即可生成字符画,效果还不错, https://www.fontke.com/tool/image2ascii/ 神注释大全 https://github.com/Blan ...
- bzoj 2165: 大楼【Floyd+矩阵乘法+倍增+贪心】
1<<i的结果需要是long long的话i是long long是没用的--要写成1ll<<i--我别是个傻子吧 虽然写的是二进制贪心,但是我觉得二分可能更好写吧(但是会慢) ...
- Java中的锁机制,你真的了解吗?
学到锁说明你已经学过多线程了,只有在多线程并发的情况下才会涉及到锁,相信大家用的最多的要数synchronized了,因为这个也是最简单的,直接加在方法上就可以使一个方法同步.那么除了synchron ...
- C#和C++的区别(一)
C#特性 1.指针可以有++.--运算,引用不可以运算: 2.类或结构的默认访问类型是internal 类的所有成员,默认是private 3.属性:用于定义一些命名特性,通过它来读取和写入相关的特性 ...
- [ USACO 2001 OPEN ] 地震
\(\\\) Description 给出一张 \(n\) 个点 \(m\) 条边的无向图,现在要建一棵生成树. 每条边都有消耗的时间 \(t_i\),也有建造的代价 \(w_i\) . 最后总金给 ...