P9134 [THUPC 2023 初赛] 拧螺丝 题解
观察样例解释,可以得到结论:在完成的前一步,老板来之前,必须有至少
为了讨论方便,下面继续用
而再往前一步呢?再上一步收走后,需要两个分别至少拧好
老板的最优策略显然是每次收走最大的那个,因此,再上一步收走前,需要三个分别至少拧好
后者需要更少的总螺丝达成,且最大值更小,因此需要准备被老板收走的其它模块需要的螺丝也更少,所以后者优于前者。
再往前一步,用类似的道理,可以证明需要四个分别至少拧好
这样的状态可以用三元组
这样取名是因为,状态可以用添加一个不完整的行的矩形来表示:
可以参考下面的可视化:
o
o
o
o oo
o oo oo
o oo ooo ooo
o oo ooo oooo ooooo ooo
容易发现,每次倒推一步相当于:
- 计算当前总个数
- 减去
k 个 - 平均地重排,多一个的列放在左侧
- 在左侧添加一列,高度与当前最左列相同
很容易写出倒推一步的程序。如果倒推了
最后再特判
那么这样就做完了……吗?
每次只能在最上面一列消除
因此,不仅需要使用高精度,而且也不再能一步一步模拟。
不过,当 remain-=k-1;width+=1;,可以直接一次性模拟多步;而
如此特殊处理后就差不多做完了……吗?
来算一下时间复杂度:需要
不过,可以注意到,
参考代码(懒得实现高精,所以直接使用了 python):
n,k=map(int,input().split(' '))
if k==1: # 特判 k=1
if n==1:
print(1)
else:
print('Poor E.S.!')
else:
if k==2: # 特判 k=2
if n<=2:
print(1)
else:
print(2**(n-2))
else:
width,height,remain=1,n,0
while width<2*k and (height>0 or (height==0 and remain>0)): # width 很小时,无需一次模拟多步
cnt=width*height+remain
cnt=cnt-k
height=cnt//width
remain=cnt-height*width
width+=1
if remain!=0:
remain+=1
while height>0 or (height==0 and remain>0): # width 现在很大,必须一次模拟多步
if remain>=2:
mult=(remain-2)//(k-1)
remain=remain-mult*(k-1)
width+=mult
height-=1
width+=1
remain=width-(k-remain)
print(width-1)