CF1560F1/F2 Nearest Beautiful Number

思路

开始蠢了,复杂度写假了。本质直接暴力贪心就行,根本不用枚举到底用了哪些数。

题目要找最小,在满足$k$限制的情况下可以选$n$的最长前缀,保证从最小的数找起。如果$n$本身就满足$k$限制,则$n$即为答案。反之取最长前缀,此时确定所用的$k$个数,在此基础上确定后面的数位上的数。如果没有能满足大于的数(最长前缀后第一位选取的数一定与$n$不同,否则最长前缀可以延长),则需要将前缀部分$+1$,后面补$0$,得到新的$n$重新找答案(此时保证新的答案最小,可以证明这样的操作至多进行$\log_{10}n$次)。如果有满足大于的数,则后面的所有位取$k$个数中的最小即可。

总复杂度为$\mathcal{O}((\log_{10}n)^2)$。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def lower_bound(nums,x):
for i in range(len(nums)):
if nums[i]>=x:
return i
return -1

def check(n,k,num,pos,_n,wid):
x=num
nums=dict()
while x>0:
nums.setdefault(x%10,0)
x//=10
nums=list(nums.keys())
nums.sort()
flag=0
for i in range(pos,wid):
tmp=int(_n[i])
x=lower_bound(nums,tmp) if flag==0 else 0
if x==-1:
return -1
if flag==0 and nums[x]>tmp:
flag=1
num=num*10+nums[x]
return num

def work(n,k):
res=10**11
_n=str(n)
wid=len(_n)
vis=dict()
tmp=0
cnt=0
for i in range(wid):
num=int(_n[i])
vis.setdefault(num,0)
if vis[num]==1:
tmp=tmp*10+num
continue
if cnt==k:
res=check(n,k,tmp,i,_n,wid)
if res==-1:
return work((tmp+1)*(10**(wid-i)),k)
return res
cnt+=1
vis[num]=1
tmp=tmp*10+num
return tmp

_t=int(input())
for _c in range(_t):
n,k=map(int,input().split())
print(work(n,k))