P2511 [HAOI2008]木棍分割 题解
思路
这道题是一个比较经典的题,特别是通过一个问题的约束去求解下一个问题的这个思维。
对于第一问,由于是要求总长度最大的那一段最小,不难发现其具有单调性,我们可以采用二分解决。我们二分最大的那段的长度
第二问,我们采用动态规划算法。首先对原数组做一个前缀和,得到
最后的答案就是
我们发现转移过来的是连续的一段区间,所以我们采用前缀和优化,用
那么转移式子变成了:
思路要点
-
要你求多个不同的问题时,不妨看看能否先求出其中任意一个,再通过这一个求出其它的。
像这一题就是先求出第一问,再通过第一问的约束求出第二问。
-
动态规划需要较低的转移时间且能转移过来的是一段区间的话,可以试一下能否用前缀和优化。
代码
#include <bits/stdc++.h>
#define re register
#define L(i, a, b) for(re int i = a; i <= b; i++)
#define R(i, a, b) for(re int i = a; i >= b; i--)
using namespace std;
const int N = 50010, M = 1010, mod = 10007;
int n, m, ans, len, a[N], p[N], sum[N], f[N], g[N];//f和g两数组第一维没用,优化掉了
void upd(int &x, const int y){
x = y;
if(x < 0) x += mod;//加法比取模快多了,等价于 x %= mod
if(x >= mod) x -= mod;//减法比取模快多了,等价于 x = (x % mod + mod) % mod
}
bool check(const int x){
re int cnt = 0, sum = 0;
L(i, 1, n){
if(a[i] > x) return 0;
if(sum + a[i] > x) sum = a[i], cnt++;
else sum += a[i];
}
return cnt < m;//等价于cnt + 1 <= m,因为最后一段没算进去,所以cnt要加1
}
int main(){
scanf("%d%d", &n, &m), m++;
L(i, 1, n) scanf("%d", &a[i]);
re int l = 1, r = 1e9, id = 1;
while(l <= r){
int mid = l + r >> 1;
if(check(mid)) r = mid - 1, len = mid;//len为第一问答案
else l = mid + 1;
}
L(i, 1, n){
sum[i] = sum[i - 1] + a[i];
if(sum[i] <= len) f[i] = 1;
while(id <= n && sum[i] - sum[id] > len) id++;
p[i] = id;
}
L(j, 1, n) upd(g[j], g[j - 1] + f[j]);
upd(ans, ans + f[n]);
L(i, 2, m){
L(j, 1, n) upd(f[j], g[j - 1] - (p[j] - 1 < 0? 0 : g[p[j] - 1]));
L(j, 1, n) upd(g[j], g[j - 1] + f[j]);
upd(ans, ans + f[n]);
}
printf("%d %d\n", len, ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】