考慮到有 \(|i - j|\),所以肯定是讓相鄰的一部分在一個團裡。
考慮構造出一個最長長度的,後面類似複製幾遍即可。
考慮到 \(|k - 1| = k - 1\),且因為 \(a_i\) 為一個排列,差的絕對值至少為 \(1\),所以只考慮兩端最長長度也只可能為 \(k\)。
不妨先假設最長長度為 \(k\) 來構造。
那麼就相當於要讓 \(a_1 = x, a_k = x + 1\)。
那麼對於 \(a_2\),\(|k - 1| - |k - 2| = 1\),那麼如果 \(|a_k - a_2| - |a_k - a_1| = 1\),就有 \(|k - 1| - |a_k - a_1| = |k - 2| + |a_k - a_2| = k\),這是合法的。
於是便可以考慮令 \(a_2 = a_1 - 1\)。
類似的,令 \(a_{k - 1} = a_k + 1\)。
以此類推,有 \(a_i = a_{i - 1} - 1, a_i = a_{i + 1} + 1\),顯然應該找到個臨界點讓左邊的 \(a_i\) 用左邊的方式,右邊的 \(a_i\) 用右邊的方式構造。
考慮到如果選取 \(\lfloor\frac{k}{2}\rfloor + \lceil\frac{k}{2}\rceil = k\),那麼這部分最劣的距離為 \(2(\lceil\frac{k}{2}\rceil - 1) \le k + 1 - 2 = k - 1\le k\),合法。
於是按照這個方法構造即可。
時間複雜度 \(O(n)\)。
程式碼
#include<bits/stdc++.h>
inline void solve() {
int n, k; scanf("%d%d", &n, &k);
for (int l = 1, r; l <= n; l = r + 1) {
r = std::min(l + k - 1, n);
int len = r - l + 1, p = len + 1 >> 1, c0 = l + p - 1, c1 = r;
for (int i = 1; i <= p; i++) printf("%d ", c0--);
for (int i = 1; i <= len - p; i++) printf("%d ", c1--);
}
puts("");
printf("%d\n", (n + k - 1) / k);
for (int l = 1, r, c = 0; l <= n; l = r + 1) {
r = std::min(l + k - 1, n), c++;
for (int i = l; i <= r; i++) printf("%d ", c);
}
puts("");
}
int main() {
int T; scanf("%d", &T);
while (T--) solve();
return 0;
}