【USACO 2020 January Silver】Loan Repayment
题目:
题目描述
Farmer John 欠了 Bessie N 加仑牛奶(1≤N≤10^12)。他必须在 K 天内将牛奶给 Bessie。但是,他不想将牛奶太早拿出手。另一方面,他不得不在还债上有所进展,所以他必须每天给 Bessie 至少 M 加仑牛奶(1≤M≤10^12)。
以下是 Farmer John 决定偿还 Bessie 的方式。首先他选择一个正整数 X。然后他每天都重复以下过程:
(1)假设 Farmer John 已经给了 Bessie G 加仑,计算 (N−G)/X 向下取整。令这个数为 Y。
(2)如果 Y 小于 M,令 Y 等于 M。
(3)给 Bessie Y 加仑牛奶。
求 X 的最大值,使得 Farmer John 按照上述过程能够在 K 天后给 Bessie 至少 N 加仑牛奶 (1≤K≤10^12)。
输入
输入仅有一行,包含三个空格分隔的正整数 N、K 和 M,满足 K⋅M<N。
注意这个问题涉及到的整数规模需要使用 64 位整数类型(例如,C/C++ 中的“long long”)。
输出
输出最大的正整数 X,使得按照上述过程 Farmer John 会给 Bessie 至少 N 加仑牛奶。
样例输入
10 3 3
样例输出
2
数据范围限制
测试点 1-3 满足 K≤10^5。
测试点 4-10 没有额外限制。
提示
在这个测试用例中,当 X=2 时 Farmer John 第一天给 Bessie 5 加仑,后两天每天给 Bessie M=3 加仑。
解题方法:
这道题目的方法是二分答案+数学。
对于 30 30 30%的数据
我们只要二分答案,然后直接模拟
k
k
k天就行了。
时间复杂度为
O
(
k
log
n
2
)
O(k\log_n^2)
O(klogn2)。
对于 100 100 100%的数据
我们要二分答案
x
x
x,对上面的办法进行优化。
假设当天要给的牛奶数是
y
y
y,一共有连续
a
a
a天要给一样的数量,然而还有
g
g
g个牛奶没给,还剩下
t
t
t天要给牛奶。
那么,我们可以知道第
a
a
a天要给
y
y
y个
(
1
)
\:(1)
(1),第
a
+
1
a+1
a+1天给的数量一定比
y
y
y要小
(
2
)
\:(2)
(2)。
则
⌊
g
−
(
a
−
1
)
y
x
⌋
=
y
(
1
)
\lfloor \frac{g-(a-1)y}{x} \rfloor=y\:(1)
⌊xg−(a−1)y⌋=y(1)
⌊
g
−
a
y
x
⌋
<
y
(
2
)
\lfloor \frac{g-ay}{x} \rfloor<y\:(2)
⌊xg−ay⌋<y(2)
简化 1 1 1式:
把向下取整去掉,得
g
−
(
a
−
1
)
y
x
≥
y
\frac{g-(a-1)y}{x}\geq y
xg−(a−1)y≥y
两边同时乘以
x
x
x,得
g
−
(
a
−
1
)
y
≥
x
y
{g-(a-1)y}\geq xy
g−(a−1)y≥xy
两边同时除以
y
y
y,得
g
y
−
(
a
−
1
)
≥
x
\frac{g}{y}-(a-1)\geq x
yg−(a−1)≥x
拆括号,得
g
y
−
a
+
1
≥
x
\frac{g}{y}-a+1\geq x
yg−a+1≥x
整理出关于
a
a
a的式子,得
g
y
−
x
+
1
≥
a
\frac{g}{y}-x+1\geq a
yg−x+1≥a
简化 2 2 2式:
把向下取整去掉,得
g
−
a
y
x
<
y
\frac{g-ay}{x}<y
xg−ay<y
两边同时乘以
x
x
x,得
g
−
a
y
<
x
y
g-ay<xy
g−ay<xy
两边同时除以
y
y
y,得
g
y
−
a
<
x
\frac{g}{y}-a<x
yg−a<x
整理出关于
a
a
a的式子,得
g
y
−
x
<
a
\frac{g}{y}-x<a
yg−x<a
整理 1 1 1和 2 2 2式:
将两个式子合并,得
g
y
−
x
<
a
≤
g
y
−
x
+
1
\frac{g}{y}-x<a\leq \frac{g}{y}-x+1
yg−x<a≤yg−x+1
因为
a
a
a是整数,所以
a
=
⌊
g
y
−
x
+
1
⌋
a=\lfloor \frac{g}{y}-x+1 \rfloor
a=⌊yg−x+1⌋
所以我们只要进行分块计算,也就是一次计算
a
a
a次,这样就会提高效率。
但是有时候
a
a
a会大于你剩下的天数
t
t
t,就会越界,所以
a
=
min
(
⌊
g
y
−
x
+
1
⌋
,
t
)
a=\min(\lfloor \frac{g}{y}-x+1 \rfloor,t)
a=min(⌊yg−x+1⌋,t)
我们只需要这样做:将
g
g
g减去
a
a
a乘以
y
y
y,得到剩下要给的牛奶数,然后再将
t
t
t减去
a
a
a,得到剩下的天数。
注:上面的运算仅限于
y
>
m
y>m
y>m。
如果
y
<
=
m
y<=m
y<=m,我们应该直接把剩下的所有都拿走,也就是将
g
g
g减去
y
y
y乘以
t
t
t,然后
t
=
0
t=0
t=0。
这是判断
x
x
x是否合法的程序:
int ck(long long x)
{
long long g=n,t=k,y,a;
while(g>0&&t>0)
{
y=g/x;
if(y<=m) g=g-t*m,s=0;
else a=min(g/y-x+1,t),g=g-a*y,t-=a;
}
if(g<=0) return 1;
return 0;
}
这道题就这样被解决了,时间复杂度大概为 O ( n log n 2 ) O(\sqrt{n}\log_{n}^{2}) O(nlogn2),可以过 n ≤ 1 0 12 n\leq10^{12} n≤1012的数据。