Problem F
数论分块
话说正解是可以直接对m-1进行分块算值,然而自己没太理解,用了另一种分块方式。
题意
给你两个数 n , m ( n , m ≤ 1 e 8 ) n, m(n, m\leq1e8) n,m(n,m≤1e8), 你需要选择两个数 x , y ( x , y ≥ 0 , n − x > 0 ) x, y(x,y \geq 0, n-x>0) x,y(x,y≥0,n−x>0), 使得 m + y m+y m+y 是 n − x n-x n−x 的倍数,请最小化 x + y x+y x+y 。
思路
首先如果
n
≥
m
n\geq m
n≥m ,可以直接得出答案为
n
−
m
n-m
n−m,下考虑
n
<
m
n<m
n<m 的情况。
设
i
=
n
−
x
i = n - x
i=n−x,对于每一个
i
i
i, 可以在
O
(
1
)
O(1)
O(1) 内求出对应的
x
+
y
x+y
x+y,于是考虑暴力枚举
i
∈
[
1
,
n
]
i\in [1, n]
i∈[1,n], 可以在
O
(
n
)
O(n)
O(n) 内得出答案。
可以用数论分块优化这一过程,对于
m
i
\frac{m}{i}
im ,在
i
∈
[
l
,
r
]
,
其
中
r
=
m
/
(
m
/
l
)
i \in [l,r],其中r = m/(m/l)
i∈[l,r],其中r=m/(m/l) 内的值是一样的。
先给出结论:答案即为
[
1
,
m
]
[1,m]
[1,m] 按上式数论分块过程中取到的所有
f
(
l
)
,
f
(
r
)
f(l),f(r)
f(l),f(r) 的最小值。
(
l
,
r
≤
n
)
(l,r\leq n)
(l,r≤n)
其中函数
f
(
i
)
f(i)
f(i) 表示当
n
n
n 变为
i
i
i 时,对应的
x
+
y
x+y
x+y 值。不多赘述了,有问题可以看代码。
下面给出具体做法即简略证明:
- 举个例子,比如 n = 10 , m = 11 , l = 6 n = 10,m = 11,l=6 n=10,m=11,l=6, 在 i ∈ [ 6 , 10 ] i\in [6,10] i∈[6,10] 内, 10 i = 1 \frac{10}{i} = 1 i10=1,容易发现在这个区间内,只有 l l l 可能成为最终答案,为什么呢?就拿这个例子来说,对于 i ∈ [ 6 , 10 ] i\in [6, 10] i∈[6,10] ,它们的 y y y 依次为 1,3,5,7,9,而它们的 x x x 每次会少1,即 f ( i ) f(i) f(i) 是随 i i i 的增大而递增的,即 f ( l ) f(l) f(l) 是这个区间的最优解。
- 但
y
y
y 不一定是单调递增的,比如把上例换为
m
=
10
m=10
m=10 ,对于
i
∈
[
6
,
10
]
i\in [6, 10]
i∈[6,10] ,它们的
y
y
y 依次为 2,4,6,8,0,这也容易理解,因为
10
∣
10
10|10
10∣10,所以不需要*2-10,在这种情况下,
r
r
r 可能取到这个区间的最小值。但 0 仅可能出现在分块的右端点,因为一旦出现 0,对于下一个
i
i
i, 它就在另一个块里了。 其实也好证明,
但没必要,比如 5 ∣ 10 5|10 5∣10,但10/5=2,而10/6=1 ,即 6 在下一个块里。
时间复杂度: 即分块的复杂度,
O
(
n
)
O(\sqrt n)
O(n)
有人可能要问
y
y
y 单增
x
x
x 单减为什么
f
(
i
)
f(i)
f(i) 一定单增?,因为
x
x
x 每次一定减少1,而
y
y
y 每次增加的也至少为1,所以
f
(
i
)
f(i)
f(i) 一定是单增的。
代码
int f(int x) { //计算当n减小到x时,n和m变化之和
int ans = (n - x) + (m+x-1)/x * x - m;
return ans;
}
void solve() {
cin >> n >> m;
if(n >= m) {
cout << n - m << endl;
return;
}
int ans = INF;
for(int l = 1, r; l <= n; l = r + 1) {
ans = min(ans, f(l));
r = min(n, m / (m / l));
if(m % r == 0)
ans = min(ans, f(r));
}
cout << ans << endl;
}