Codeforces 1948E Clique Partition

rizynvu發表於2024-03-16

考慮到有 \(|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;
}

相關文章