Codeforces Round 840題解(A、B、C)

lightmon發表於2024-06-08

A. Absolute Maximization

我們可以選擇兩個位置\(i, j\)來存放最大值\(a_i\)和最小值\(a_j\),對每一位,如果從\(a_{[1, n]}\)的這一位有\(1\),我們就可以把\(1\)挪到\(a_i\)裡,如果這意味有\(0\),我們就可以把\(0\)挪到\(a_j\)裡,這樣就可以構造出最大的\(a_i\)和最小的\(a_j\)

int n;
int a[550];

void solve() {
    cin >> n;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
    }
    int max_v = 0, min_v = 1023;
    for (int r = 9; r >= 0; r --) {
        for (int i = 1; i <= n; i ++) {
            if (a[i] >> r & 1) {
                max_v |= 1 << r;
            }
            else {
                min_v &= ~(1 << r);
            }
        }
    }
    cout << max_v - min_v << '\n';
}

B. Incinerate

將怪物按照\(h\)從小到大排序,每次打出\(k\)的傷害就相當於把所有\(h \le\)\(i\)次傷害總和的怪物擊倒。那麼我們每次可以二分找出剩下的怪物的起始位置。

當某一次\(k\)降到\(\le 0\)後,就不可能再打倒新的怪物,如果此時還沒有把所有怪物都打倒的話,那麼就不可能打倒所有的怪物了。

模擬這個過程,最壞情況是\(k\)每次都減一,因此時間複雜度最壞為\(klogn+nlogn\)

int n, k;
PII a[N];
int min_v[N];

void solve() {
    cin >> n >> k;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i].first;
    }
    for (int i = 1; i <= n; i ++) {
        cin >> a[i].second;
    }
    sort(a + 1, a + 1 + n);
    min_v[n + 1] = INF;
    for (int i = n; i >= 1; i --) {
        min_v[i] = min(min_v[i + 1], a[i].second);
    }
//    for (int i = 1; i <= n; i ++) {
//        cout << min_v[i] << ' ';
//    }
//    cout << '\n';
    int cur = k;
    while (1) {
        int pos = lower_bound(a + 1, a + 1 + n, PII(cur + 1, 0)) - a - 1;
//        cout << pos << '\n';
        if (pos >= n) {
            cout << "YES" << '\n';
            return;
        }
        int cnt = k - min_v[pos + 1];
//        cout << cnt << '\n';
        cur += cnt;
        k = cnt;
        if (cnt <= 0) {
            cout << "NO" << '\n';
            return;
        }
    }
}

C. Another Array Problem

有點噁心的一道題,我最後\(15\)分鐘才突然產生思路,但是分類有點小問題加沒能調出來。。。結束後看分數,納尼!竟然是\(2000\)分的題。。。

觀察到如果最大值所在位置左邊或者右邊有\(\ge 2\)個元素,我們就可以把這個方向上所有元素都變成最大值。

例如:\(2, 5, 3, 4\) -> \(2, 5, 1, 1\) -> \(2, 5, 0, 0\) -> \(2, 5, 5, 5\)

實際上,如果最大值所在位置左邊或右邊有\(\ge 2\)個元素,我們可以把陣列中所有元素都變成最大值。

例如:\(2, 5, 3, 4\) -> \(2, 5, 5, 5\) -> \(3, 3, 5, 5\) -> \(0, 0, 5, 5\) -> \(5, 5, 5, 5\)

那麼就只剩下兩種特別情況了,當\(n = 2\)時,只有兩種方案,換或不換,直接輸出比較即可。

\(n = 3\)並且最大值在中間時比較噁心,我們注意到在換的過程中出現在最左邊或者最右邊的數都可以被用來覆蓋整個陣列。我們可以發現出現在左邊的數最大是\(a[1]\)或者\(a[2] - a[1]\),出現在右邊的數最大是\(a[3]\)\(a[2] - a[3]\),因為只要換一次,原本的最大值肯定就不保了,所以把這四個情況和不換的情況綜合起來取\(max\)就是答案。但是如果要求具體詳細嚴謹的證明,比較麻煩。蒟蒻想不出來,想出來也表達不清楚啊。。。

int n;
int a[M];

void solve() {
    cin >> n;
    int max_v = 0;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        max_v = max(max_v, a[i]);
    }
    vector<int> bag;
    for (int i = 1; i <= n; i ++) {
        if (a[i] == max_v) {
            bag.push_back(i);
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i ++) {
        ans += a[i];
    }
    if (n == 2) {
        cout << max(ans, abs(a[1] - a[2]) * 2) << '\n';
        return;
    }
    if (n >= 4) {
        cout << n * max_v << '\n';
        return;
    }
    if (a[1] == max_v || a[3] == max_v) {
        cout << n * max_v << '\n';
        return;
    }
    cout << max(ans, max({a[2] - a[1], a[2] - a[3], a[1], a[3]}) * 3) << '\n';
}

相關文章