这道题第一眼直接一个二分板子把第一问解决掉,然后主要是统计方案。

其实这个方程还不算难推,只要推出来朴素 \(dp\) ,之后的一步一步也很顺理成章,所以这种题主要看能不能静下心来慢慢做。

solution

第一问就是一个 Monthly Expense S ,本片题解只说第二问。

朴素 \(dp\)

也没几个变量,所以很轻松的可以设计状态 \(f[i][j]\) 表示当前到第 \(i\) 段,一共分了 \(j\) 次的方案数。

我们思考如何来表示把几段放在一大段里面,也就可以推出来转移方程。

如果我们新开一段,那么 \(f[i][j] += f[i-1][j-1]\)

如果我们把这段和上一段拼到一起,那么 \(f[i][j] += f[i - 1][j - 1]\)

以此类推,规律很显然,所以可以写出 \(dp\) 方程。

\[f[i][j] += f[k][j - 1]
\]

能从 \(k\) 转移的要求是从 \(k\) 加到 \(i\) 的木棍长度不大于第一问的答案 。

其实 \(dp\) 方程没有那么难推,只要设计好状态分情况讨论就行了。

时间复杂度是 \(O(n^2m)\) ,因为 \(k\) 需要枚举。

显然时间空间都不行,所以考虑优化。

空间优化

这个不难想到,因为 \(f [i][j]\) 只能有 \(f[k][j-1]\) 转移而来,所以我们调换一下维数顺序和枚举顺序就行,用第一维来表示分了 \(i\) 次,第二维来表示到第 \(i\) 段。

然后就能滚动数据优化了,空间问题就解决了(其实开short也勉强不爆)

时间优化

首先,我们可以发现,对于一个 \(f[i][j]\) ,能转移到他的 \(f[i-1][k]\) 中的 \(k\) 肯定是连续的一段,并且最小的 \(k\) 的值之和 \(i\)有关。

所以我们可以考虑预处理出来 \(k\) 的值,然后直接一个前缀和解决问题。

不过处理 \(k\) 需要 \(n^2\) ,怎么办?

不难发现随着 \(i\) 的增加, \(k\) 是单调不降的,弄个单调指针就行了,均摊 \(O(n)\) 。

所以最后复杂度是 \(O(nm+n)=O(nm)\)

code

(放这么丑的代码真是对不起大家的眼睛啦)

#include <cstring>
#include <algorithm>
#include <cstdio>
#define mp make_pair
#define R register int
#define int long
#define printf Ruusupuu = printf int Ruusupuu ; using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const int N = 5e4 + 10 ;
const int M = 1e3 + 10 ;
const int P = 1e4 + 7 ; inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
} inline int J( int a , int b ){ return a + b >= P ? a + b - P : a + b ; }
inline int S( int a , int b ){ return a - b < 0 ? a - b + P : a - b ; } int len [N] , n , m , mid , lside , rside , ans , pre [N] , f [3][N] , qz [3][N] , res ; void sc(){
n = read() , m = read() ;
for( R i = 1 ; i <= n ; i ++ ) len [i] = read() , lside = max( lside , len [i] ) ;
} bool check(){
int div = 0 , now = 0 ;
for( R i = 1 ; i <= n ; i ++ ){
if( now + len [i] > mid ) div ++ , now = 0 ;
now += len [i] ;
}// printf( "%ld %ld\n" , mid , div ) ;
return div <= m ;
} void work(){
rside = (int) 5e7 + 10 , ans = 0 ;
while( lside <= rside ){
mid = ( lside + rside ) >> 1 ;
if( check() ) ans = mid , rside = mid - 1 ;
else lside = mid + 1 ;
} printf( "%ld " , ans ) ; int now = 0 , ck = 0 ;
for( R i = 1 ; i <= n ; i ++ ){
now += len [i] ;
while( now > ans ) now -= len [ck ++] ;
pre [i] = ck - 2 ; //printf( "%ld\n" , pre [i] ) ;
} f [0][0] = 1 ;
for( R i = 0 ; i <= n ; i ++ ) qz [0][i] = 1 ; for( R i = 1 ; i <= m + 1 ; i ++ ){ for( R j = 1 ; j <= n ; j ++ ){
if( pre [j] == -2 ) f [i & 1][j] = qz [( i - 1 ) & 1][j - 1] ;
else f [i & 1][j] = S( qz [( i - 1 ) & 1][j - 1] , qz [( i - 1 ) & 1][pre [j]] ) ;
qz [i & 1][j] = J( qz [i & 1][j - 1] , f [i & 1][j] ) ;
// printf( "%ld %ld %ld %ld\n" , i , j , f [i & 1][j] , qz [i & 1][j] ) ;
} for( R j = 0 ; j <= n ; j ++ ) f [( i - 1 ) & 1][j] = qz [( i - 1 ) & 1][j] = 0 ;
res = J ( res , f [i & 1][n] ) ;
} printf( "%ld\n" , res ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}

题解—P2511 [HAOI2008]木棍分割的更多相关文章

  1. 2021.12.06 P2511 [HAOI2008]木棍分割(动态规划)

    2021.12.06 P2511 [HAOI2008]木棍分割(动态规划) https://www.luogu.com.cn/problem/P2511 题意: 有n根木棍, 第i根木棍的长度为 \( ...

  2. [洛谷P2511][HAOI2008]木棍分割

    题目大意:有$n(n\leqslant5\times10^4)$根木棍,连续放在一起,把它们分成$m(\leqslant10^3)$段,要求使得最长的段最短,问最短的长度以及方案数 题解:要使得最长的 ...

  3. P2511 [HAOI2008]木棍分割

    目录 Description Solution Code Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, ...

  4. Luogu P2511 [HAOI2008]木棍分割 二分+DP

    思路:二分+DP 提交:3次 错因:二分写萎了,$cnt$记录段数但没有初始化成$1$,$m$切的次数没有$+1$ 思路: 先二分答案,不提: 然后有个很$naive$的$DP$: 设$f[i][j] ...

  5. luogu P2511 [HAOI2008]木棍分割

    传送门 第一问是一道经典的二分,二分答案\(ans\),然后从前往后扫,判断要分成几段救星了 第二问设\(f_{i,j}\)表示前\(i\)个数分成\(j\)段,每段之和不超过第一问答案的方案,转移就 ...

  6. 【题解】HAOI2008木棍分割

    对于这道题目的两问,第一问直接二分答案求出最短长度.关键在于第二问应当如何求:建立dp方程,dp[i][j]代表到第i个分界线,切了j次(强制在第i处切一刀.这样就不会对后面的状态产生影响).状态转移 ...

  7. BZOJ1044: [HAOI2008]木棍分割

    1044: [HAOI2008]木棍分割 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1580  Solved: 567[Submit][Statu ...

  8. 【BZOJ1044】[HAOI2008]木棍分割(动态规划,贪心)

    [BZOJ1044][HAOI2008]木棍分割(动态规划,贪心) 题面 BZOJ 洛谷 题解 第一问随便二分一下就好了,贪心\(check\)正确性显然. 第二问随便前缀和+单调队列优化一下\(dp ...

  9. 【BZOJ1044】[HAOI2008]木棍分割

    [BZOJ1044][HAOI2008]木棍分割 题面 bzoj 洛谷 题解 第一问显然可以二分出来的. 第二问: 设\(dp[i][j]\)表示前\(i\)个,切了\(j\)组的方案数 发现每次转移 ...

随机推荐

  1. DL基础补全计划(三)---模型选择、欠拟合、过拟合

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  2. c语言:sprintf() 数字转字符赋值给数组

    //sprintf() //sprintf 最常见的应用之一是把整数打印到字符串中,所以,spritnf 在大多数场合可以替代itoa /* 缓冲区溢出 第一个参数的长度太短了,没的说,给个大点的地方 ...

  3. 使用mvn命令将pom和jar上传至nexus私服

    要将自定义的jar或者pom上传至nexus私服,需要配置maven的settings文件! 上传至nexus私服配置 1. settings配置 <!-- maven设置私服对应的信息:id. ...

  4. [JS]闭包和词法环境

    词法环境 词法环境(lexical environment)由两个部分组成: 环境记录--一个存储所有局部变量作为其属性的对象. 对外部词法环境的引用,与外部代码相关联. 全局词法环境在脚本执行前创建 ...

  5. Redis双写一致性与缓存更新策略

    一.双写一致性 双写一致性,也就是说 Redis 和 mysql 数据同步 双写一致性数据同步的方案有: 1.先更新数据库,再更新缓存 这个方案一般不用: 因为当有两个请求AB先后更新数据库后,A应该 ...

  6. jdk源码阅读-Object类

    native 关键字 private static native void registerNatives(); static { registerNatives(); } public final ...

  7. [考试总结]noip模拟23

    因为考试过多,所以学校的博客就暂时咕掉了,放到家里来写 不过话说,vscode的markdown编辑器还是真的很好用 先把 \(noip\) 模拟 \(23\) 的总结写了吧.. 俗话说:" ...

  8. python -- 负数平方根-虚数的使用

    负数的平方根是虚数. 不能使用sqrt,因为它只能处理浮点数,而虚数是完全不同的--这也是由另外一个叫做cmath(即 complex math, 复数)的模块来实现这些功能的原因. >> ...

  9. java封装基础详解

    java封装基础详解 java的封装性即是信息隐藏,把对象的属性和行为结合成一个相同的独立单体,并尽可能地隐藏对象的内部细节. 封装的特性是对属性来讲的. 封装的目标就是要实现软件部件的"高 ...

  10. Sth about Educational DP Contest

    Contest Website : atcoder.jp/contests/dp \[\begin{array}{c|C|c|c} TaskNum & TaskName & Statu ...