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

其实这个方程还不算难推,只要推出来朴素 \(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. ES6 箭头函数你正确使用了吗

    ES6 箭头函数你正确使用了吗 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 说明 在ES6中允许使用"箭头&quo ...

  2. 让我手把手教你写一个强大、方便使用的 IOC 容器

    一.介绍 1.介绍 最近无聊,也没什么事做,没事做总是要给自己找点事情做吧,毕竟人的生活在与折腾.于是,决定自己手动写一个 IOC 的框架.我们知道在 NetCore 的版本里面已经内置了 IOC 容 ...

  3. EditPlus运行java时如何从键盘输入数据

    在练习Java的Scanner时,EditPlus如何读取从键盘输入的数呢? 例如如下程序,编译通过,运行时却输入不了数据: 1 package myP101; 2 3 import java.uti ...

  4. 在 Intenseye,为什么我们选择 Linkerd2 作为 Service Mesh 工具(Part.2)

    在我们 service mesh 之旅的第一部分中,我们讨论了"什么是服务网格以及我们为什么选择 Linkerd2?".在第二部分,我们将讨论我们面临的问题以及我们如何解决这些问题 ...

  5. ZooKeeper 分布式锁 Curator 源码 04:分布式信号量和互斥锁

    前言 分布式信号量,之前在 Redisson 中也介绍过,Redisson 的信号量是将计数维护在 Redis 中的,那现在来看一下 Curator 是如何基于 ZooKeeper 实现信号量的. 使 ...

  6. Python + unittest知识点回顾

    postman 安装Newman 先安装node.js,把npm添加到环境变量中. npm install newman --registry=https://registry.npm.taobao. ...

  7. 技术期刊 · 天光台高未百尺 | Uber 工程师的 JS 算法课;大数据时代的个人隐私;设计师的 Github;告别 PPT 工程师;从零开始实现的像素画

    蒲公英 · JELLY技术期刊 Vol.42 这是一个最好的时代,多样化的平台给了所有人成长发展的机会,各种需求和解决需求的人让人大开眼界:但这也并不是完美的时代,"前端还需要懂什么算法?& ...

  8. mysql - 按条件统计

    在表中分别统计mt =0 和 mt>0 的个数 方法一:select count(if(mt=0,1,null)) as a,count(if(mt>0,1,null)) as b fro ...

  9. DOS 常用命令集

    net use $">\\ip\ipc$Content$nbsp;" " /user:" " 建立IPC空链接 net use $"& ...

  10. Kafka流处理内幕详解

    1.概述 流处理是一种用来处理无穷数据集的数据处理引擎.通常无穷数据集具有以下几个特点: 无穷数据:持续产生的数据,它们通常会被称为流数据.例如:银行信用卡交易订单.股票交易就.游戏角色移动产生的数据 ...