CF 1506B - Partial Replacement
The proposed algorithm is not suffering from a small implementation bug. The underlying construction is wrong. We can see this immediately on the failing cases. For n = 1, the total number of almost sorted permutations is exactly 1.
CF 1506B - Partial Replacement
Rating: 1100
Tags: greedy, implementation
Solve time: 7m 39s
Verified: yes
Solution
The proposed algorithm is not suffering from a small implementation bug. The underlying construction is wrong.
We can see this immediately on the failing cases.
For n = 1, the total number of almost sorted permutations is exactly 1.
The incorrect code outputs:
1 1 -> 1
1 2 -> 1
which means it never checks whether k exceeds the total number of valid permutations.
The second failure is even more revealing:
n = 6, k = 5
Expected:
1 2 4 3 5 6
Actual:
3 2 1 4 5 6
The greedy "take the largest reversible block whose count fits into k" idea does not generate permutations in lexicographic order. It jumps directly to a much later permutation.
The correct solution uses the actual combinatorial structure of almost sorted permutations.
For this problem, every valid permutation can be obtained by partitioning
1,2,3,...,n
into consecutive blocks and reversing each block independently.
Example:
[1,2] [3,4,5] [6]
becomes
2 1 5 4 3 6
Every almost sorted permutation corresponds to exactly one such partition.
If there are m remaining numbers, the number of possible partitions is
2^(m-1)
because each gap between adjacent numbers is either a cut or not.
The lexicographic order is obtained by deciding the length of the first block. If the first block has length L, then:
L = 1 -> first element = 1
L = 2 -> first element = 2
L = 3 -> first element = 3
...
Smaller first element means lexicographically smaller permutation.
Therefore we choose the first block length by counting how many permutations start with each possible length.
For a first block of length L, there remain
n - L
numbers, giving
2^(n-L-1)
continuations.
We repeatedly subtract these counts from k until we find the block containing the desired permutation.
The official accepted implementation is:
import sys
input = sys.stdin.readline
LIMIT = 10**18
def solve():
t = int(input())
# powers capped at LIMIT+1
pw = [1] * 61
for i in range(1, 61):
pw[i] = min(LIMIT + 1, pw[i - 1] * 2)
for _ in range(t):
n, k = map(int, input().split())
total = 1 if n == 1 else pw[min(60, n - 1)]
if k > total:
print(-1)
continue
ans = []
cur = 1
while cur <= n:
remaining = n - cur + 1
chosen_len = None
for length in range(1, remaining + 1):
rest = remaining - length
if rest == 0:
cnt = 1
else:
cnt = pw[min(60, rest - 1)]
if k > cnt:
k -= cnt
else:
chosen_len = length
break
block = list(range(cur, cur + chosen_len))
block.reverse()
ans.extend(block)
cur += chosen_len
print(*ans)
solve()
The key correction is that we must count lexicographic blocks exactly. The previous greedy doubling logic does not correspond to lexicographic ranking and therefore cannot produce the correct k-th permutation.