牛客周赛 Round 122 题解

https://anoth3r.top/solution/nkwk122/

A ICPC Problems

请输入文本

void solve() {
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i) cout << (char)('A' + i) << " ";
    cout << "\n";
}

B Chess

对于象,它可以移动到 的位置,也就是说

  • 如果 ,必然一步都跳不了
  • 移动不会改变它横坐标和纵坐标的奇偶性。

我们可以把棋盘分为四个子块:

  • 奇偶性相同的点,数量为
  • 奇偶性相同的点,数量为
  • 奇偶性相同的点,数量为
  • 奇偶性相同的点,数量为

对角线移动又会保证 的值一定,在奇偶性相同的条件下,每块又会被分为两个连通块,最大连通块的大小为

枚举四个起点取最大值就行。

void solve() {
    ll n, m;
    cin >> n >> m;
    if (n <= 2 || m <= 2) cout << 1 << "\n";
    else {
        ll a1 = (n + 1) / 2, a2 = n / 2, b1 = (m + 1) / 2, b2 = m / 2;
        cout << max({(a1 * b1 + 1) / 2, (a1 * b2 + 1) / 2, (a2 * b1 + 1) / 2, (a2 * b1 + 1) / 2}) << "\n";
    }
}

C Sequence Cost

记数组和为 ,不难猜到最终的答案为 。接下来给出证明:

如果不进行修改,代价为

如果进行修改,记操作花费为 ​ ,最终数组的和为

  • 整个数组的 被修改了,此时必然有

  • 整个数组的 没有被修改,最终的数组的 至少为 ,此时总费用为

    • 如果 ,如果进行了有效的修改, 显然大于 ,故
    • 如果 ,即 ,如果进行了有效的修改, 显然大于

综上,最小代价为

void solve() {
    ll n, mn = INF, mx = 0, sum = 0;
    cin >> n;
    for (ll i = 0, x; i < n; ++i) cin >> x, sum += x, mn = min(mn, x), mx = max(mx, x);
    cout << min(mx + mn * n, sum) << "\n";
}

D Digital Deletion

如果当前的 ,即前面的数字可以组成 的所有数字,如果引入一个新的数字 就能被扩展到

  • 对于 ,还是原来的组合方式
  • 对于 ,可以通过 的组合得到。(此处 为组成 的一组方案)

用这种方式,我们就能把 拓展到 ,此时要求

这样从小到大模拟一遍就可以算出最大的 了。

  • 如果 ,显然所有数字都能被删除。

  • 对于其余情况:

    • 如果 ,它产生不了任何贡献,可以直接删。
    • 对于剩余的情况,我们贪心地拿出 的最大的数进行更新,直到 ,剩下的数字就是能删的数字。
void solve() {
    int n;
    cin >> n;
    vector<ll> a(n);
    for (int i = 0; i < n; ++i) cin >> a[i];
    sort(all(a));
 
    ll R = 0;
    for (int i = 0; i < n; ++i) {
        if (a[i] <= R + 1) R += a[i];
        else break;
    }
    if (R == 0) {
        cout << n << "\n";
        return;
    }
 
    multiset<ll> ms;
    for (int i = 0; i < n; ++i) {
        if (1 <= a[i] && a[i] <= R) ms.insert(a[i]);
    }
 
    ll cur = 0;
    int keep = 0;
    while (cur < R) {
        auto it = ms.upper_bound(cur + 1);
        if (it == ms.begin()) {
            break;
        }
        --it;
        cur += *it;
        ms.erase(it);
        ++keep;
    }
    cout << n - keep << "\n";
}

E Block Array

好数组就是连接的几个块,所以我们贪心地进行分割,若当前位置值为 ,则要求区间 内所有值都等于 ,若满足则把这段当作一个块,下一起点变为 。对每个 记录能连续切出的块个数 ,那么以 为左端的好子数组个数就是 ,总体答案就是

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 2, 0);
    for (int i = 1; i <= n; ++i) cin >> a[i];
    vector<int> R(n + 2, 0);
    R[n + 1] = n + 1, R[n] = n;
    for (int i = n; i >= 1; --i) {
        if (i < n && a[i] == a[i + 1]) R[i] = R[i + 1];
        else R[i] = i;
    }
    vector<int> cnt(n + 3, 0);
    ll ans = 0;
    for (int i = n; i >= 1; --i) {
        int k = a[i], r = i + k - 1;
        if (k <= 0 || r > n) {
            cnt[i] = 0;
        } else {
            if (R[i] >= r) {
                int j = i + k;
                if (j <= n) cnt[i] = 1 + cnt[j];
                else cnt[i] = 1;
            } else {
                cnt[i] = 0;
            }
        }
        ans += cnt[i];
    }
    cout << ans << "\n";
}

F Sequence Covering

我们需要构建一个字典序最大的序列。这意味着我们希望从左到右尽可能让每一个位置的数字最大。 在每一个位置 (当前还没有确定的位置),我们有两个选择:

  1. 延续上一次操作的值:如果我们之前进行了一个操作,当前位置 可以被包含在那个操作的延伸区间中(或者被一个新的与之前值相同的操作覆盖,代价为 )。如果之前的“活跃值” 比我们在当前预算 下能找到的新的最大值还要大,那么花费 1 的代价将 延伸到 是最优的。
  2. 开始一个新的区间操作:如果在当前位置 和预算 允许的范围 内,能找到一个比 更大的值(或者没有活跃值,或者 不够大),我们就应该去获取这个最大值。假设最大值 出现在下标 ,我们执行操作区间 。这样 都会变成 ,花费是 。为了给后面的操作保留尽可能多的预算,如果有多个相同的最大值 ,我们选择最左边的一个(下标 最小)。
void solve() {
    int n;
    ll k;
    cin >> n >> k;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    ST<int> st(n);
    st.init(a);

    vector<int> ans(n + 1);
    int i = 1;
    int las = -1;

    while (i <= n) {
        ll lim = i + k;
        int r = (lim > n) ? n : lim;

        auto [mv, pos] = st.getMax(i, r);
        pos = -pos;

        if (las > mv && k >= 1) {
            ans[i] = las;
            k--;
            i++;
        } else {
            int cost = pos - i;

            for (int j = i; j <= pos; j++) {
                ans[j] = mv;
            }

            k -= cost;
            las = mv;
            i = pos + 1;
        }
    }

    for (int j = 1; j <= n; j++) cout << ans[j] << " ";
    cout << "\n";
}

头文件

//Another
#include<bits/stdc++.h>
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define all(a) a.begin(), a.end()
#define rall(a) a.rbegin(), a.rend()
using namespace std;

using ll = long long;
using ld = long double;
using ull = unsigned long long;
using i128 = __int128;
using PII = pair<int, int>;
using PLL = pair<ll, ll>;
using TLLL = tuple<ll, ll, ll>;

constexpr int inf = (ll)1e9 + 7;
constexpr ll INF = (ll)2e18 + 9;
// constexpr ll INF = (ll)4e18;
// constexpr ll MOD = 1e9 + 7;
constexpr ll MOD = 998244353;
constexpr ld eps = 1e-10;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ull randint(ull l, ull r) {uniform_int_distribution<unsigned long long> dist(l, r); return dist(rng);}

void init() {

}

void solve() {

}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    init();

    int t = 1;
    cin >> t;
    for (int _ = 1; _ <= t; ++_) {
        solve();
    }

    return 0;
}

ST表

template<class Int>
struct ST {
    const int n, k;
    vector<vector<pair<Int, int>>> Max;
    vector<vector<pair<Int, int>>> Min;
    ST(int n) : n(n), k(__lg(n)) {
        Max.resize(k + 1, vector<pair<Int, int>>(n + 1));
        Min.resize(k + 1, vector<pair<Int, int>>(n + 1));
    }
    template<class Array>
    void init(Array &a) {
        for (int i = 1; i <= n; i++) {
            Max[0][i] = {a[i], -i};
            Min[0][i] = {a[i], -i};
        }
        for (int i = 0, t = 1; i < k; i++, t <<= 1) {
            const int T = n - (t << 1) + 1;
            for (int j = 1; j <= T; j++) {
                Max[i + 1][j] = max(Max[i][j], Max[i][j + t]);
                Min[i + 1][j] = min(Min[i][j], Min[i][j + t]);
            }
        }
    }
    pair<Int, int> getMax(int l, int r) {
        if (l > r) {
            swap(l, r);
        }
        int k = __lg(r - l + 1);
        return max(Max[k][l], Max[k][r - (1 << k) + 1]);
    }
    pair<Int, int> getMin(int l, int r) {
        if (l > r) {
            swap(l, r);
        }
        int k = __lg(r - l + 1);
        return min(Min[k][l], Min[k][r - (1 << k) + 1]);
    }
};