问题描述
请你通过若干次除法将一个数字 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]);
}