题解—P2511 [HAOI2008]木棍分割
这道题第一眼直接一个二分板子把第一问解决掉,然后主要是统计方案。
其实这个方程还不算难推,只要推出来朴素 \(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\) 方程。
\]
能从 \(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]木棍分割的更多相关文章
- 2021.12.06 P2511 [HAOI2008]木棍分割(动态规划)
2021.12.06 P2511 [HAOI2008]木棍分割(动态规划) https://www.luogu.com.cn/problem/P2511 题意: 有n根木棍, 第i根木棍的长度为 \( ...
- [洛谷P2511][HAOI2008]木棍分割
题目大意:有$n(n\leqslant5\times10^4)$根木棍,连续放在一起,把它们分成$m(\leqslant10^3)$段,要求使得最长的段最短,问最短的长度以及方案数 题解:要使得最长的 ...
- P2511 [HAOI2008]木棍分割
目录 Description Solution Code Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, ...
- Luogu P2511 [HAOI2008]木棍分割 二分+DP
思路:二分+DP 提交:3次 错因:二分写萎了,$cnt$记录段数但没有初始化成$1$,$m$切的次数没有$+1$ 思路: 先二分答案,不提: 然后有个很$naive$的$DP$: 设$f[i][j] ...
- luogu P2511 [HAOI2008]木棍分割
传送门 第一问是一道经典的二分,二分答案\(ans\),然后从前往后扫,判断要分成几段救星了 第二问设\(f_{i,j}\)表示前\(i\)个数分成\(j\)段,每段之和不超过第一问答案的方案,转移就 ...
- 【题解】HAOI2008木棍分割
对于这道题目的两问,第一问直接二分答案求出最短长度.关键在于第二问应当如何求:建立dp方程,dp[i][j]代表到第i个分界线,切了j次(强制在第i处切一刀.这样就不会对后面的状态产生影响).状态转移 ...
- BZOJ1044: [HAOI2008]木棍分割
1044: [HAOI2008]木棍分割 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1580 Solved: 567[Submit][Statu ...
- 【BZOJ1044】[HAOI2008]木棍分割(动态规划,贪心)
[BZOJ1044][HAOI2008]木棍分割(动态规划,贪心) 题面 BZOJ 洛谷 题解 第一问随便二分一下就好了,贪心\(check\)正确性显然. 第二问随便前缀和+单调队列优化一下\(dp ...
- 【BZOJ1044】[HAOI2008]木棍分割
[BZOJ1044][HAOI2008]木棍分割 题面 bzoj 洛谷 题解 第一问显然可以二分出来的. 第二问: 设\(dp[i][j]\)表示前\(i\)个,切了\(j\)组的方案数 发现每次转移 ...
随机推荐
- MindInsight:一款基于MindSpore框架的训练可视化插件
技术背景 在深度学习或者其他参数优化领域中,对于结果的可视化以及中间网络结构的可视化,也是一个非常重要的工作.一个好的可视化工具,可以更加直观的展示计算结果,可以帮助人们更快的发掘大量的数据中最有用的 ...
- pod调度
Pod调度 在默认情况下,一个pod在哪个node节点上运行,是由scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的. 但是在实际过程中,这并不满足需求,因为很多情况下,我们想控 ...
- Spring Boot 2.x基础教程:使用@Scheduled实现定时任务
我们在编写Spring Boot应用中经常会遇到这样的场景,比如:我需要定时地发送一些短信.邮件之类的操作,也可能会定时地检查和监控一些标志.参数等. 创建定时任务 在Spring Boot中编写定时 ...
- C语言:位运算符
异或 ^ 两个二进制位相同结果为0:不相同结果为1 1^1=0 1^0=1 0^1=1 0^0=1 按位或 | 两个二进制位 ...
- python twain 扫描
def multiScan(self): """ Scan and return an array of PIL objects If no images, will r ...
- Unittest方法 -- 用例执行顺序
import unittestfrom selenium import webdriverclass F4(unittest.TestCase): @classmethod def setUpClas ...
- 6 Java基础整理 第六-八章
1.封装 封装的目的是简化编程和增强安全性. 简化编程是指,封装可以让使用者不必了解具体类的内部实现细节,而只是要通过提供给外部访问的方法来访问类中的属性和方法 增强安全性是指,封装可以使某个属性只能 ...
- 在 Golang 中实现一个简单的Http中间件
本文主要针对Golang的内置库 net/http 做了简单的扩展,通过添加中间件的形式实现了管道(Pipeline)模式,这样的好处是各模块之间是低耦合的,符合单一职责原则,可以很灵活的通过中间件的 ...
- String类型转成int类型
在将String类型转换成int类型时: int n = Interger.parseInt(Stringnum); 如果报错,可以改成 int n = Interger.parseInt(Strin ...
- Jmeter 学习 搭建(1)
功能 1.web自动化测试 2.接口测试 3.压力测试 4.性能测试 5.通过jdbc进行数据库测试 6.java测试 优缺点 优点 1.开源,可扩展性好 2.GUI界面,小巧灵活 3.100% j ...