1. 程式人生 > 其它 >Codeforces Round #783 (Div. 2)

Codeforces Round #783 (Div. 2)

Codeforces Round #783 (Div. 2)

A - Direction Change

保證讓\(n \geq m\), 只要\(m > 1\) 在第二維上就可以反覆橫跳,則必定有解

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> m;
        if (m > n) swap(n, m);
        if (n - m > 1 && m < 2) cout << -1 << '\n';
        else if (n - m < 2) cout << n + m - 2 << '\n';
        else if (n - m & 1) cout << 2 * n - 3 << '\n';
        else cout << 2 * n - 2 << '\n';
    }
    return 0;
}

B - Social Distance

先拆環

假設最後一個人坐在最後一個位置,則第一人必定坐在\(max(a_n, a_1) + 1\)的位置上,這樣才能保證兩人在換上相隔人數合法,

之後下一個人的位置\(s_i = s_{i - 1} + max(a_{i -1}, a_i) + 1\),只要保證最後一個人位置不超過拆環的序列長度即可,

假定$a_0 = a_n, b_i = max(a_{i - 1}, a_i) $

然後就是貪心,使得兩人之間所需空位差距儘量最小,排個序即可

int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n >> m;
        bool f = 1;
        for (int i = 1; i <= n; ++i) cin >> a[i];
        sort(a + 1, a + 1 + n);
        a[0] = a[n];
        for (int i = 1; i <= n; ++i) b[i] = max(a[i], a[i - 1]);
        for (int i = 1, j = 0; i <= n && f; ++i) {
            j += b[i] + 1;
            if (j > m) f = 0;
        }
        cout << (f ? "YES" : "NO") << '\n';
    }
    return 0;
}

C - Make it Increasing

模擬題意暴力即可,必定存在一個位置\(i\)不用操作,且其前面的數都要進行 減 操作,其後面的數都需要 加 操作,

暴力去列舉這個\(i\)即可

int main() {
    IOS;
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    long long mx = 2e18;
    for (int i = 1; i <= n; ++i) {
        long long cur = 0, ls = 0;
        for (int j = i - 1; j; --j) {
            long long c = ls / a[j] + 1;
            cur += c; ls = a[j] * c;  
        }
        ls = 0;
        for (int j = i + 1; j <= n; ++j) {
            long long c = ls / a[j] + 1;
            cur += c; ls = a[j] * c;  
        }
        umin(mx, cur);
    }
    cout << mx;
    return 0;
}

D - Optimal Partition

dp, \(f_i\) 表示以\(i\)結尾分割後的最大值,則

\(f_i = \left\{\begin{matrix} max(f_j + i - j)& \sum_{k = 1}^i a_k > \sum_{k = 1}^j a_k\\ max(f_j) & \sum_{k = 1}^i a_k = \sum_{k = 1}^j a_k\\ max(f_j - i + j) & \sum_{k = 1}^i a_k < \sum_{k = 1}^j a_k \end{matrix}\right. \quad i > j \geq 0\)

\(\left\{\begin{matrix} f_i - i = max(f_j - j)& \sum_{k = 1}^i a_k > \sum_{k = 1}^j a_k\\ f_i = max(f_j) & \sum_{k = 1}^i a_k = \sum_{k = 1}^j a_k\\ f_i + i= max(f_j + j) & \sum_{k = 1}^i a_k < \sum_{k = 1}^j a_k \end{matrix}\right. \quad i > j \geq 0\)

一維偏序用for迴圈搞定,對於字首和的偏序可以通過數軸對映,離散化在數軸上表示字首和

再隨便找資料結構維護數軸上的 \(f_i - i, f_i, f_i + i\) 的區間最值資訊即可

本人選擇線段樹,一棵即能維護這三種資訊在區間最值的資訊

const int N = 5e5 + 5;
 
struct BIT {
    struct node {
        int l, r, val[3];
    } tr[N << 2];
    void build(int p, int l, int r) {
        tr[p] = {l, r, {-1000000, -1000000, -1000000}};
        if (l == r) return;
        int mid = l + r >> 1;
        build(p << 1, l, mid); build(p << 1 | 1, mid + 1, r);
    }
    void change(int p, int k, int v, int id) {
        if (tr[p].l == k && tr[p].r == k) {
            umax(tr[p].val[0], v - id);
            umax(tr[p].val[1], v);
            umax(tr[p].val[2], v + id);
            return;
        }
        int mid = tr[p].l + tr[p].r >> 1;
        if (mid >= k) change(p << 1, k, v, id);
        else change(p << 1 | 1, k, v, id);
        rep(i, 0, 2) umax(tr[p].val[i], max(tr[p << 1].val[i], tr[p << 1 | 1].val[i]));
    }
    int ask(int p, int l, int r, int k) {
        if (tr[p].l >= l && tr[p].r <= r) return tr[p].val[k];
        int mid = tr[p].l + tr[p].r >> 1;
        if (mid >= r) return ask(p << 1, l, r, k);
        else if (mid < l) return ask(p << 1 | 1, l, r, k);
        return max(ask(p << 1, l, r, k), ask(p << 1 | 1, l, r, k));
    }
} bit;
 
int n, m, _, k, cas;
long long a[N], b[N];
 
int main() {
    IOS;
    for (cin >> _; _; --_) {
        cin >> n;
        vector<long long> c(1, 0);
        for (int i = 1; i <= n; ++i)
            cin >> a[i], c.emplace_back(a[i] += a[i - 1]); 
        sort(all(c)); c.erase(unique(all(c)), c.end());
        m = c.size();
        for (int i = 1; i <= n; ++i) b[i] = lower_bound(all(c), a[i]) - c.begin() + 1;
        bit.build(1, 1, m);
 
        int cur;
        bit.change(1, lower_bound(all(c), 0) - c.begin() + 1, 0, 0);
        for (int i = 1, ls = 0; i <= n; ++i, ls = cur) {
            cur = (a[i] - a[i - 1] > 0 ? 1 : a[i] - a[i - 1] < 0 ? -1 : 0) + ls;
            long long t = bit.ask(1, b[i], b[i], 1);
            umax(cur, bit.ask(1, b[i], b[i], 1));
            if (b[i] > 1) umax(cur, bit.ask(1, 1, b[i] - 1, 0) + i);
            if (b[i] < m) umax(cur, bit.ask(1, b[i] + 1, m, 2) - i);
            bit.change(1, b[i], cur, i);
        }
        cout << cur << '\n';
    }
    return 0;
}

E - Half Queen Cover

不考慮斜著。則就是\(n\)個半皇后佔據\(n\)\(n\)列,考慮斜著,也就是\(k\)個半皇后形成的對角線正好拼成了一g個\((n - k) \times (n - k)\)的矩形

如圖,\(k = 7\) 半皇后用行列覆蓋了\(k \times k\)矩形,並額外還覆蓋到了肉色的兩個矩形,並通過\(n - k\)條對角線覆蓋了\((n - k) \times (n - k)\)的矩形

邊長為\(n - k\)的正方形有\((n - k) * 2 - 1\) 條對角線,則 \(k \geq 2 \times n - 2 \times k - 1\)

\(k = \left \lceil \frac{2n - 1}{3} \right \rceil\), 之後就是構造\(k\)個半皇后覆蓋\(k * k\)矩形的問題

int main() {
    IOS;
    cin >> n; m = (2 * n + 1) / 3;
    cout << m << '\n';
    for (int i = 1, j = 1; i <= m; ++i, j += 2) {
        if (j > m) j = 2;
        cout << i << ' ' << j << '\n';
    }
    return 0;
}