原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=5224
毁灭
Description
小S 的面前有一个Boss,Boss 具有n 的攻击力和m 的生命值,而小S 除了1 点生命值以外就穷得只剩钱了,所幸小S 可以用钱购买能力,具体来说,她可以以价格a 购买1 点攻击力,以价格b 购买1 点防御力,以价格c 购买一点生命值,此外,小S 是一个熟练的膜法师,所以她可以以价格d 购买一点膜法值。战斗开始时,首先小S 以时间的力量对Boss 造成等同于膜法值的伤害,之后由Boss 先手,双方轮流对对方造成Max(己方攻击-对方防御,0)的伤害,直到一方生命值非正,判定另一方获胜。现在小S 想知道,她若想战胜Boss 至少需要多少钱。
注意,Boss 防御力为0。T不大于1e5,n, m不大于2*1e5
Input
第一行一个整数T 表示测试数据组数。
接下来T 行,每行六个正整数n m a b c d,含义如上,这些数均不超过1e8。
N,M<=200000 T<=10^5
Output
T 行,每行一个整数表示对应的答案。
Sample Input
2
9 5 6 9 6 4
5 6 3 8 3 6
Sample Output
20
33
题解
先想想暴力,攻击力的值域为 [ 0 , m ] [0,m] [0,m],防御力值域为 [ 0 , n ] [0,n] [0,n],膜法值值域为 [ 0 , m ] [0,m] [0,m],只有生命值不好枚举,但是在已知前 3 3 3个量之后我们就可以直接计算生命值了,复杂度为 O ( T × n m 2 ) O(T\times nm^2) O(T×nm2)。
让我们考虑一下防御值对生命值与防御值代价的影响,设
b
o
s
s
boss
boss的攻击次数为
t
t
t,当前防御值为
d
e
f
,
d
e
f
∈
[
0
,
n
]
def,def\in[0,n]
def,def∈[0,n],就有:
c
o
s
t
=
t
×
(
n
−
d
e
f
)
×
c
+
d
e
f
×
b
=
t
×
n
×
c
+
(
b
−
t
×
c
)
d
e
f
cost=t\times (n-def)\times c+def\times b\\ =t\times n\times c+(b-t\times c)def
cost=t×(n−def)×c+def×b=t×n×c+(b−t×c)def
因为 d e f def def的收益为线性,所以取值只可能为 0 0 0或 n n n,而当 d e f = n def=n def=n时,我们并不需要多余的攻击力,可以直接特判 a t k = 1 , d e f = n atk=1,def=n atk=1,def=n的情况,综上我们在实际计算时只需要考虑 d e f = 0 def=0 def=0的情况。
现在我们的枚举项只有
a
t
k
atk
atk(攻击力)和
o
r
z
orz
orz(膜法值)了,考虑一下这两者间的关系:
如果
o
r
z
orz
orz的代价小于等于
a
t
k
atk
atk,我们就直接全买膜法开局秒杀
b
o
s
s
boss
boss;
如果
o
r
z
+
=
a
t
k
orz+=atk
orz+=atk后少挨了一次打,减少生命值支出后更划算,我们就可以一直
o
r
z
+
=
a
t
k
orz+=atk
orz+=atk,最后变成全膜法;
如果前两种情况都不满足,我们就只维持最低的膜法,就是消掉
m
%
a
t
k
m\ \%\ atk
m % atk这一零碎部分,要不然干脆余数也不消了,直接砍死。
综上,对于膜法,我们要么直接特判一炮轰死,要不然就在枚举过程中讨论消余数和直接砍死的情况。
所以我们只用枚举 a t k atk atk就行了,我们需要考虑的只有 ⌈ m a t k ⌉ \lceil\frac{m}{atk}\rceil ⌈atkm⌉不同的 m \sqrt{m} m个取值,最终复杂度 O ( T × m ) O(T\times \sqrt{m}) O(T×m)。
代码
#include<cstdio>
#define min(a,b) (a<b?a:b)
int T,n,m,l,r,t;
long long a,b,c,d,ans;
void in(){scanf("%d%d%lld%lld%lld%lld",&n,&m,&a,&b,&c,&d);}
void ac()
{
for(ans=min(m*d,a+n*b),l=1,r,t;l<=m;l=r+1)t=m/l,r=m/t,ans=min(ans,min((t+1)*c*n+l*a,t*c*n+(m-t*r)*d+r*a));
printf("%lld\n",ans);
}
main(){for(scanf("%d",&T);T--;)in(),ac();}