问题描述
请你通过若干次除法将一个数字 x x x 变得不超过 y y y。
现在有很多个除数可以选择,给定一个长度为 n n n 的序列 b b b。表示除数 i i i 的花费是 b i b_i bi, 你可以用这个除数把 x x x 变为 x / i x / i x/i。每种除数可以使用无限次。
多组询问,每组询问输入两个数 x , y x, y x,y,表示需要使用若干次除法把 x x x 变得不超过 y y y,对于每一组询问你都要输出一个答案表示求最小花费,保证题目一定有解。
输入格式
输入包含 q + 2 q + 2 q+2 行。
第一行输入两个正整数 n , q n, q n,q。
第二行输入 n n n 个正整数,第 i i i 个数表示 b i b_i bi。 接下来 q q q 行,每行两个正整数 x , y x, y x,y,如题意所示。
输出格式
输出 q q q 行。 每行输出一个数,表示对应询问的答案。
样例
样例输入1:
5 3
4 3 2 2 4
4 3
1 4
5 1
样例输出1:
2
0
2
数据范围
对于 20 % 20\% 20% 的数据, 1 ≤ n , q , x , y ≤ 1 0 2 1 \le n, q, x, y \le 10^2 1≤n,q,x,y≤102。
对于 40 % 40\% 40% 的数据, 1 ≤ n , q , x , y ≤ 1 0 3 1 \le n, q, x, y \le 10^3 1≤n,q,x,y≤103。
对于 100 % 100\% 100% 的数据, 1 ≤ n , q , x , y ≤ 1 0 5 1 \le n, q, x, y \le 10^5 1≤n,q,x,y≤105, 1 ≤ b i ≤ 1 0 9 1 \le b_i \le 10^9 1≤bi≤109。
题解
首先,我们考虑正着想,用 d p i dp_{i} dpi 表示到 i i i 的最小花费。
因此, d p i = d p x / k + a k dp_{i} = dp_{x / k} + a_k dpi=dpx/k+ak,复杂度为 O ( n 2 × T ) O(n^2 \times T) O(n2×T)。
由于多组数据, x x x 不同,因此每次都要算一下 d p dp dp 数组,不能进行预处理。
也可以写记忆化搜索,比 dp
稍快,能得 50 50 50 分。
考虑反向思考,从 1 1 1 开始乘, d p i dp_{i} dpi 表示造成 i i i 点削减的最小花费。
初始化 d p i = min ( d p j ) { 1 ≤ j ≤ n } dp_{i} = \min(dp_{j})\{1 \le j \le n\} dpi=min(dpj){1≤j≤n}。
接下来完全背包预处理,结束时同样进行上面操作即可。
对于每次询问,只需找出 ⌊ x p ⌋ ≤ y \lfloor\frac{x}{p}\rfloor \le y ⌊px⌋≤y 的 p p p 的最小值即可。
可以使用二分和直接计算。
二分单调性证明: d p i = min ( d p j ) { 1 ≤ j ≤ n } dp_i = \min(dp_j)\{1 \le j \le n\} dpi=min(dpj){1≤j≤n},对于 d p i dp_i dpi 和 d p i + 1 dp_{i + 1} dpi+1, d p i + 1 dp_{i + 1} dpi+1 一定小于等于 min ( d p i + 1 , d p i ) \min(dp_{i + 1}, dp_i) min(dpi+1,dpi)。
memset(a, 0x3f, sizeof(a));
输入
for(int i = n - 1; i >= 1; -- i){a[i] = min(a[i], a[i + 1]);
}
//dp
for(int i = 1; i <= 100000; ++ i){for(int j = 1; j <= 100000 / i; ++ j){a[i * j] = min(a[i * j], a[i] + a[j]);}
}
for(int i = 1e5 - 1; i >= 1; -- i){a[i] = min(a[i], a[i + 1]);
}
a[1] = 0;
while(q --){int x, y;scanf("%d %d", &x, &y);printf("%d\n", a[x / (y + 1) + 1]);
}