题解:
最坏情况指的是出错位置始终在最长的区间里。为了使时间最少,我们每次一定是尽量均分区间。
设dp[n]表示,在最坏情况下,处理n行代码所需要的最少时间。
那么有
dp[n] = min{(i - 1) * p + dp[ceil(n / i)]} + r
意义是,将n行代码分成i块,那么需要加i - 1个printf,然后对最长的区间递归处理。
直接枚举i是O(n^2)的。
优化在对i的枚举,因为ceil(n / i)的取值最多有O(sqrt(n))种,而且取值相同的i都在连续的一段内,那么为了花费最小,我们肯定是取最小i。(类似的问题在【【SPOJ-NAGAY】Joseph’s Problem】遇到过,那个问题是floor(n / i))
然后我们就要推导下标啦。
总之就是要解这个方程
x、i、n为非负整数,已知n、i,求最小的x
有两种方法
第一种:看Claris博客,得到x = (n - 1) / ((n - 1) / i) + 1(这里的除均为整除)
第二种:
首先要知道
(具体数学里面有)
带入,得到
同乘上x
因为我们要求x的最小值,所以我们只看左边的不等式
因为x为非负整数,左边不一定是整数,那么x的最小值应该是
复杂度:
时间复杂度:O(n^(3/4))(不会分析...),空间复杂度:O(n)
GET:
多推公式
/* Telekinetic Forest Guard */
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 1000005;
int n, r, p;
LL dp[maxn];
inline int iread() {
int f = 1, x = 0; char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
return f * x;
}
inline int ceil(int x, int b) {
return (x + b - 1) / b;
}
inline LL dfs(int x) {
if(x <= 1) return 0;
if(dp[x]) return dp[x];
LL res = (LL)(x - 1) * p;
for(int i = 2; i < x; i = ceil(x, (ceil(x, i) - 1)))
res = min(res, (LL)(i - 1) * p + dfs(ceil(x, i)));
return dp[x] = res + r;
}
int main() {
n = iread(); r = iread(); p = iread();
printf("%lld\n", dfs(n));
return 0;
}