Rayan Programming Contest 2024 - Selection (Codeforces Round 989, Div. 1 + Div. 2) 補題記錄(A~E)

yhbqwq發表於2024-12-01

A

答案顯然就是 \(\text{lcm}(a,b)\)

void run() {
    int T = read();
    while (T--) {
        int a = read(), b = read();
        cout << a / __gcd(a, b) * b << '\n';
    }
}

B

考慮貪心模擬。若當前列舉到位置 \(i\) 時恰有 \(m\) 個連續的 \(0\),則從這個位置開始將後面連續的 \(k\) 個位置全部賦值為 \(1\)。容易證明這是正確的。

時間複雜度為 \(O(n)\)

char s[N];
void run() {
    int T = read();
    while (T--) {
        int n = read(), m = read(), k = read();
        scanf("%s", s + 1);
        int cnt = 0, sum = 0;
        for (int i = 1; i <= n; ++i) {
            if (s[i] == '0') {
                ++cnt;
                if (cnt >= m) {
                    ++sum;
                    int j, x;
                    for (j = i, x = 1; j <= n && x <= k; ++j, ++x)
                        s[j] = '1';
                    i = j - 1;
                    cnt = 0;
                }
            } else {
                cnt = 0;
            }
        }
        cout << sum << '\n';
    }
}

C

考慮這樣的一個策略:

  • 若存在一個只包含 ? 的四連通塊滿足該塊內 ? 的數目不小於 \(2\),則考慮任選兩個相鄰的 ? 並讓它們互相只想對方,其餘的 ? 都指向這兩個 ? 中的一個。
  • 若某一個 ? 四聯通的某一個位置不是 ?,且從該位置出發會無限迴圈,則讓這個 ? 指向這個位置。
  • 剩下的位置隨便指向任意一個位置。

容易證明該策略正確。若把指向關係顯示建圖則形成一個基環森林,維護的話可以使用並查集和 dfs 找環。總時間複雜度為 \(O(\alpha nm)\)

程式碼可能有點難寫。

char s[2010][2010];
int id[2010][2010], fa[N], fm[N];
int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}
const int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
int vis[2010][2010], n, m, dfn;
int dfs(int x, int y) {
    assert(s[x][y] != '?');
    if (x < 1 || y < 1 || x > n || y > m) return 0;
    if (vis[x][y] == dfn)
        return 1;
    else if (vis[x][y] && fm[id[x][y]])
        return 1;
    else if (vis[x][y])
        return 0;
    vis[x][y] = dfn;
    if (s[x][y] == 'U') {
        if (dfs(x - 1, y)) {
            fm[id[x][y]] = 1;
            return 1;
        }
    }
    if (s[x][y] == 'D') {
        if (dfs(x + 1, y)) {
            fm[id[x][y]] = 1;
            return 1;
        }
    }
    if (s[x][y] == 'L') {
        if (dfs(x, y - 1)) {
            fm[id[x][y]] = 1;
            return 1;
        }
    }
    if (s[x][y] == 'R') {
        if (dfs(x, y + 1)) {
            fm[id[x][y]] = 1;
            return 1;
        }
    }
    return 0;
}
void run() {
    // freopen("1.in", "r", stdin);
    // freopen("1.out", "w", stderr);
    int T = read(), ca = 1;
    while (T--) {
        n = read(), m = read();
        for (int i = 1; i <= n; ++i)
            scanf("%s", s[i] + 1);
        // if (ca++ == 41) {
        //     for (int i = 1; i <= n; ++i, cerr << '\n')
        //         for (int j = 1; j <= m; ++j) cerr << s[i][j];
        // }
        dfn = 0;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                if (s[i][j] == '?')
                    for (int d = 0; d < 4; ++d) {
                        int ii = i + dx[d], jj = j + dy[d];
                        if (ii >= 1 && ii <= n && jj >= 1 && jj <= m && s[ii][jj] == '?') {
                            if (d == 0) s[i][j] = 'R', s[ii][jj] = 'L';
                            else if (d == 2) s[i][j] = 'L', s[ii][jj] = 'R';
                            else if (d == 1) s[i][j] = 'D', s[ii][jj] = 'U';
                            else s[i][j] = 'U', s[ii][jj] = 'D';
                            break;
                        }
                    }
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                if (s[i][j] == '?')
                    for (int d = 0; d < 4; ++d) {
                        int ii = i + dx[d], jj = j + dy[d];
                        if (ii >= 1 && ii <= n && jj >= 1 && jj <= m) {
                            if (s[ii][jj] != '?') {
                                int ok = 0;
                                if (s[ii][jj] == 'L' && d == 0) s[i][j] = 'R', ok = 1;
                                else if (s[ii][jj] == 'R' && d == 2) s[i][j] = 'L', ok = 1;
                                else if (s[ii][jj] == 'U' && d == 1) s[i][j] = 'D', ok = 1;
                                else if (s[ii][jj] == 'D' && d == 3) s[i][j] = 'U', ok = 1;
                                if (ok) break;
                            }
                        }
                    }
        // for (int i = 1; i <= n; ++i, cout << '\n')
        //     for (int j = 1; j <= m; ++j) cout << s[i][j];
        int idx = 0;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) {
                ++idx;
                id[i][j] = idx;
                fa[idx] = idx;
                fm[idx] = 0;
                vis[i][j] = 0;
            }
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                if (s[i][j] != '?')
                    ++dfn, dfs(i, j);
        // for (int i = 1; i <= n; ++i, cout << '\n')
        //     for (int j = 1; j <= m; ++j) cout << s[i][j];
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                if (s[i][j] != '?') {
                    int ii, jj;
                    if (s[i][j] == 'L') ii = i, jj = j - 1;
                    else if (s[i][j] == 'R') ii = i, jj = j + 1;
                    else if (s[i][j] == 'U') ii = i - 1, jj = j;
                    else ii = i + 1, jj = j;
                    if (ii >= 1 && ii <= n && jj >= 1 && jj <= m) {
                        int x = find(id[i][j]), y = find(id[ii][jj]);
                        if (x != y) fm[y] |= fm[x], fa[x] = y;
                    }
                }
        // for (int i = 1; i <= n * m; ++i) cout << fm[i] << ' ';
        // cout << '\n';
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                if (s[i][j] == '?')
                    for (int d = 0; d < 4; ++d) {
                        int ii = i + dx[d], jj = j + dy[d];
                        if (ii >= 1 && ii <= n && jj >= 1 && jj <= m) {
                            assert(s[ii][jj] != '?');
                            if (fm[find(id[ii][jj])]) {
                                if (d == 0) s[i][j] = 'R';
                                else if (d == 2) s[i][j] = 'L';
                                else if (d == 1) s[i][j] = 'D';
                                else s[i][j] = 'U';
                                int x = find(id[ii][jj]), y = find(id[i][j]);
                                if (x != y) fm[y] |= fm[x], fa[x] = y;
                                break;
                            }
                        }
                    }
        int cnt = 0;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                cnt += fm[find(id[i][j])];
        // for (int i = 1; i <= n; ++i, cout << '\n')
        //     for (int j = 1; j <= m; ++j) cout << s[i][j];
        cout << cnt << '\n';
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) {
                id[i][j] = 0;
                fa[idx] = 0;
                s[i][j] = 0;
                fm[idx] = 0;
                vis[i][j] = 0;
            }
    }
}

D

比 C 簡單。考慮用 set 來維護當前所有 \(0,1,2\) 的位置。每一次找到最後一個 \(0\) 和最前一個 \(1\)(需要滿足最後一個 \(0\) 在最前一個 \(1\) 後面)或最後一個 \(2\) 和最前一個 \(1\)(需要滿足最後一個 \(2\) 在最前一個 \(1\) 後面)交換。容易證明該策略正確。判斷當前陣列是否有序可以維護當前所有出現過的不同數字,然後用 set 找到當前數字最前和最後的位置就行了。

時間複雜度為 \(O(n\log n)\),自帶大常數(霧

int a[N];
void run() {
    int T = read(), ca = 1;
    while (T--) {
        int n = read();
        for (int i = 1; i <= n; ++i)
            a[i] = read();
        set<int> buc[3];
        for (int i = 1; i <= n; ++i)
            if (a[i] == 0) buc[0].insert(i);
            else if (a[i] == 1) buc[1].insert(i);
            else buc[2].insert(i);
        auto chk = [&](void) {
            int cnt = 0;
            vector<int> id;
            for (int i = 0; i < 3; ++i)
                if (buc[i].size()) ++cnt, id.eb(i);
            if (cnt == 1) return 1;
            else if (cnt == 2) {
                if (*buc[id[0]].rbegin() < *buc[id[1]].begin()) return 1;
                return 0;
            } else {
                if (*buc[0].rbegin() < *buc[1].begin() && *buc[1].rbegin() < *buc[2].begin()) return 1;
                return 0;
            }
        };
        vector<pair<int, int>> op;
        while (!chk()) {
            if (buc[0].size() && buc[1].size()) {
                int x = *buc[0].rbegin(), y = *buc[1].begin();
                if (x > y) {
                    op.eb(x, y);
                    buc[0].erase(x);
                    buc[1].erase(y);
                    buc[0].insert(y);
                    buc[1].insert(x);
                }
            }
            if (chk()) break;
            if (buc[1].size() && buc[2].size()) {
                int x = *buc[1].rbegin(), y = *buc[2].begin();
                if (x > y) {
                    op.eb(x, y);
                    buc[1].erase(x);
                    buc[2].erase(y);
                    buc[1].insert(y);
                    buc[2].insert(x);
                }
            }
        }
        cout << op.size() << '\n';
        for (auto &[x, y] : op)
            cout << x << ' ' << y << '\n';
    }
}

E

大分類討論。

  • \(n=1\)。此時若 \(k=1\) 則需構造排列 \(\lbrace 1\rbrace\)。否則無解。
  • \(n\neq 1\)\(k=1\)。此時顯然無解。
  • \(2\mid k\)。先考慮 \(k=2\) 的特殊情況。顯然可以構造 \(\lbrace 1,2,3,\ldots,n\rbrace\)\(\lbrace n,n-1,n-2,\ldots,1\rbrace\)。同樣的若 \(2\mid k\) 則可以構造 \(1\sim n\) 組成的排列中字典序前 \(\frac{k}{2}\) 小的排列和字典序前 \(\frac{k}{2}\) 的排列。特殊點,若 \(n!<k\) 則無解。
  • \(k-1\mid n-1\)。此時令 \(d=\frac{n-1}{d-1}\),可以構造 \(k\) 個排列。其中第 \(i\) 個排列(\(i<k\))為 \(\lbrace 1+(d-1)i,2+(d-1)i,\ldots,n,1,2,\ldots,(d-1)i\rbrace\)。最後一個排列直接讓每一個位置所對應元素的和相等即可。
  • \(2\nmid n\)。此時考慮到 \(k\ge 3\)\(2\nmid k\),所以考慮先用上面的方法構造 \(k=3\) 的排列,對於另外 \(n-3\) 個排列用偶數的構造方法。特殊的,若 \(n!+3<k\) 則無解。
  • 其餘情況無解。

時間複雜度為 \(O(nk)\),可以透過。

int sjh[N], p1[N], p2[N], n, k;
void run() {
    int T = read(), ca = 1;
    while (T--) {
        n = read(), k = read();
        if (n == 1) {
            if (k == 1) {
                cout << "YES\n";
                for (int i = 1; i <= k; ++i) cout << "1\n";
            } else {
                cout << "NO\n";
            }
        } else if (k == 1) cout << "NO\n";
        else if (~k & 1) {
            int fac = 1;
            if (n > 12) fac = LLONG_MAX;
            else {
                for (int i = 1; i <= n; ++i)
                    fac = fac * i;
            }
            if (fac < k) cout << "NO\n";
            else {
                cout << "YES\n";
                iota(a + 1, a + n + 1, 1ll);
                for (int i = 0; i < k / 2; ++i) {
                    for (int j = 0; j < n; ++j) cout << a[j + 1] << ' ';
                    cout << '\n'; 
                    next_permutation(a + 1, a + n + 1);
                }
                iota(a + 1, a + n + 1, 1ll);
                reverse(a + 1, a + n + 1);
                for (int i = 0; i < k / 2; ++i) {
                    for (int j = 0; j < n; ++j) cout << a[j + 1] << ' ';
                    cout << '\n'; 
                    prev_permutation(a + 1, a + n + 1);
                }
            }
        } else {
            if (n == k) {
                cout << "YES\n";
                for (int i = 1; i <= n; ++i, cout << '\n') {
                    int j = i;
                    for (int x = 1; x <= n; ++x) {
                        cout << j << ' ';
                        ++j;
                        if (j > n) j = 1;
                    }
                }
            } else {
                if ((n - 1) % (k - 1) == 0) {
                    cout << "YES\n";
                    for (int i = 0; i < n; ++i) sjh[i] = 0;
                    int dt = (n - 1) / (k - 1), st = 1;
                    while (k--) {
                        if (k) {
                            int x = st;
                            for (int i = 0; i < n; ++i) {
                                cout << x << ' ';
                                sjh[i] += x;
                                ++x;
                                if (x > n) x = 1;
                            }
                            cout << '\n';
                        } else {
                            int all = st + sjh[0];
                            for (int i = 0; i < n; ++i)
                                cout << all - sjh[i] << ' ';
                            cout << '\n';
                        }
                        st += dt;
                    }
                } else {
                    int fac = 1;
                    if (n > 12) fac = LLONG_MAX;
                    else {
                        for (int i = 1; i <= n; ++i)
                            fac = fac * i;
                    }
                    fac -= 3;
                    if (fac < k) cout << "NO\n";
                    else if (n % 2 == 1) {
                        cout << "YES\n";
                        vector<vector<int>> p; p.resize(3);
                        for (int i = 0; i < 3; ++i) p[i].resize(n);
                        fill(sjh, sjh + n, 0ll);
                        for (int i = 0; i < n; ++i) p[0][i] = i + 1, sjh[i] = i + 1;
                        int st = n / 2 + 1; for (int i = 0; i < n; ++i) {
                            p[1][i] = st; ++st; if (st > n) st = 1;
                            sjh[i] += p[1][i];
                        }
                        int all = n + sjh[0];
                        for (int i = 0; i < n; ++i) p[2][i] = all - sjh[i];
                        iota(p1 + 1, p1 + n + 1, 1ll);
                        iota(p2 + 1, p2 + n + 1, 1ll);
                        reverse(p2 + 1, p2 + n + 1);
                        k -= 3; k /= 2;
                        for (int i = 0; i < 3; ++i, cout << '\n')
                            for (int j = 0; j < n; ++j) cout << p[i][j] << ' ';
                        while (k--) {
                            int o[6] = {1, 1, 1, 1, 1, 1};
                            for (int i = 1; i <= n; ++i) if (p1[i] != p[0][i - 1]) o[0] = 0;
                            for (int i = 1; i <= n; ++i) if (p1[i] != p[1][i - 1]) o[1] = 0;
                            for (int i = 1; i <= n; ++i) if (p1[i] != p[2][i - 1]) o[2] = 0;
                            for (int i = 1; i <= n; ++i) if (p2[i] != p[0][i - 1]) o[3] = 0;
                            for (int i = 1; i <= n; ++i) if (p2[i] != p[1][i - 1]) o[4] = 0;
                            for (int i = 1; i <= n; ++i) if (p2[i] != p[2][i - 1]) o[5] = 0;
                            if (count(o, o + 6, 1ll) == 0) {
                                for (int i = 1; i <= n; ++i) cout << p1[i] << ' '; cout << '\n';
                                for (int i = 1; i <= n; ++i) cout << p2[i] << ' '; cout << '\n';
                            } else ++k;
                            next_permutation(p1 + 1, p1 + n + 1);
                            prev_permutation(p2 + 1, p2 + n + 1);
                        }
                    } else {
                        cout << "NO\n";
                    }
                }
            }
        }
    }
}

相關文章