牛客周赛 Round 135 题解

由于牛客的渲染问题,你可以点此链接进入我的博客查看

A Add Three

模拟和直接计算均可。

模拟:

void solve() {
    int n;
    cin >> n;
    int p = 0;
    while (p + 3 <= n) p += 3;
    cout << p << endl;
}

直接计算:

void solve() {
    int n;
    cin >> n;
    cout << n / 3 * 3 << endl;
}

B Maximize The Count

记录一下每个数字值与下标之差,取最大即可。

void solve() {
    int n, mx = 0;
    cin >> n;
    unordered_map<int, int> c;
    for (int i = 1, x; i <= n; ++i) cin >> x, c[x - i]++;
    for (auto [u, v] : c) mx = max(mx, v);
    cout << mx << endl;
}

C Permutation Swapping

不难发现当 的时候可以选择任意 ,进行 的置换,因此必然有解。

剩下的情况特判一下相等即可, 的时候还需要特判一下是否是逆序。

void solve() {
    int n;
    cin >> n;
    bool f = 1, nf = 1;
    for (int i = 1, x; i <= n; ++i) {
        cin >> x;
        if (x != i) f = 0;
        if (x != n - i + 1) nf = 0;
    }
    if (f || n >= 4 || n == 3 && nf)
        cout << "YES" << endl;
    else
        cout << "NO" << endl;
}

D Two Options

由于两个操作对于总值的影响分别为 ,因此最终的和必然大于等于初始的 ,那么最优解显然是取最终值为 ,又因为必然大于等于,所以减少的次数一定小于增加的次数,于是我们可以把所有减少的次数都与增加的次数绑组,贪心修改即可。本题需要注意负数的上取整处理。

void solve() {
    int n;
    ll sum = 0;
    cin >> n;
    vector<ll> a(n);
    for (int i = 0; i < n; ++i) cin >> a[i], sum += a[i];
    ll p = sum > 0 ? (sum + n - 1) / n : sum / n, chg = 0;
    for (int i = 0; i < n; ++i) {
        if (a[i] < p) chg += p - a[i];
    }
    cout << chg << endl;
}

E Not Equal

我们需要求解序列 ,使得对于所有 ,都有 ,修改的代价为:

  • 如果 ,由于每次加 花费 ,总代价为
  • 如果 ,由于每次减 花费 ,总代价为
  • 如果 ,代价为

直观来看,修改某个数是为了让它避开与相邻元素的冲突。假设某位置的最优取值为 ,如果它距离初始值 非常远(例如 ),那么在离 更近的方向上,比如正数区间 之间,必然存在某个值既不等于 也不等于 ,因为这 个数的候选区间里,左右相邻元素最多只会占据 个名额,至少剩下 个合法的候选值可选。

因此,将 调整到离 更近的合法值,可以严格减小(或维持)修改该元素的代价,同时不破坏相邻不相同的约束。

根据以上结论,我们使用动态规划进行求解,用 表示处理前 个数,且第 个数的最终值为 中的第 个候选值时,所需的最少总花费。有:

用滚动 实现。

void solve() {
    int n;
    cin >> n;
    vector<ll> a(n), b(n), c(n);
    for (int i = 0; i < n; ++i) cin >> a[i];
    for (int i = 0; i < n; ++i) cin >> b[i];
    for (int i = 0; i < n; ++i) cin >> c[i];

    array<ll, 5> v{}, dp{};
    int siz = 0;
    for (int i = 0; i < n; ++i) {
        array<ll, 5> nv{}, ndp{};
        int nsiz = 0;
        for (ll j = a[i] - 2; j <= a[i] + 2; ++j) {
            if (j >= 1) {
                nv[nsiz++] = j;
            }
        }

        for (int j = 0; j < nsiz; ++j) {
            ll V = nv[j], cost = 0;

            if (V > a[i])
                cost = (V - a[i]) * b[i];
            else if (V < a[i])
                cost = (a[i] - V) * c[i];

            if (i == 0) {
                ndp[j] = cost;
            } else {
                ll mn = INF;
                for (int k = 0; k < siz; k++) {
                    if (v[k] != V) {
                        mn = min(mn, dp[k]);
                    }
                }
                ndp[j] = mn + cost;
            }
        }

        for (int j = 0; j < nsiz; ++j) {
            v[j] = nv[j];
            dp[j] = ndp[j];
        }
        siz = nsiz;
    }

    ll ans = INF;
    for (int j = 0; j < siz; ++j)
        if (dp[j] < ans) ans = dp[j];
    cout << ans << endl;
}

F Alone

我们先考虑任意一个格子 ,如果它是孤立点,那么剩下的 的格子能够自由染色。这些格子组成了独立于第 行和第 列的一个大矩形。因此,有 种方法来填充这部分 。

然后我们考虑预设点:

对于我们选定的

  • 如果某个预设点 位于第 行或第 列,但不是 本身,那么它与 的孤立配置是矛盾的。这种情况下, 永远不可能成为孤立点,贡献为
  • 如果所有 个预设点都不与 的孤立配置矛盾,那么这些预设点就必须满足它们各自的条件。
    • 每个位于 自由区域内的预设点,都必须是黑色。这个要求将该区域的自由度减半。
    • 如果 ​ 本身就是一个预设点,它的黑色要求与孤立配置一致,不产生矛盾。

设有 个不同行, 个不同列有预设点。

对于每一个不是预设点的格子,一共有 个格子。对于其中任何一个格子 ,它的孤立配置与 个预设点都不矛盾。这 个点全部落在 的自由区域内。为了满足这 个点的黑色要求,自由区域的填法从 种减少到了 种。

  • 此部分贡献

对于每一个是预设点,且不冲突的格子,记一共有 个这样的格子。它的孤立配置与其他的 个预设点都不矛盾。这 个点全部落在 的自由区域内。为了满足这 个点的黑色要求,自由区域的填法从 种减少到了 种。

  • 此部分贡献

总贡献即为:

其中 可能达到 量级,需要用费马小定理进行优化。

所以

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    vector<PII> a(k);
    unordered_map<int, int> X, Y;
    for (int i = 0; i < k; ++i) cin >> a[i].fi >> a[i].se, X[a[i].fi]++, Y[a[i].se]++;
    int xx = X.size(), yy = Y.size();
    int K = 0;
    for (auto [x, y] : a)
        if (X[x] == 1 && Y[y] == 1) K++;
    ll mod = 998244352;
    cout << (Z(1) * (n - xx) * (m - yy) + Z(2) * K) * ksm(Z(2), ((n - 1) * (m - 1) % mod - k + mod) % mod) << endl;
}

头文件

#include <bits/stdc++.h>
#define pb push_back
#define pf push_front
#define pob pop_back
#define pof pop_front
#define eb emplace_back
#define fi first
#define se second
#define all(a) a.begin(), a.end()
#define rall(a) a.rbegin(), a.rend()
#define endl "\n"
using namespace std;

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

void init() {
}

void solve() {
}

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

    init();

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

    return 0;
}

自动取模

constexpr int MOD = 998244353;

template <class T>
constexpr T ksm(T a, ll b) {
    T res = 1;
    while (b) {
        if (b & 1) res *= a;
        b >>= 1;
        a *= a;
    }
    return res;
}

template <int P>
struct MInt {
    int x;
    constexpr MInt() : x() {}
    constexpr MInt(ll x) : x{norm(x % getMod())} {}
    static int Mod;
    constexpr static int getMod() {
        if (P > 0) {
            return P;
        } else {
            return Mod;
        }
    }
    constexpr static void setMod(int Mod_) { Mod = Mod_; }
    constexpr int norm(int x) const {
        if (x < 0) {
            x += getMod();
        }
        if (x >= getMod()) {
            x -= getMod();
        }
        return x;
    }
    constexpr int val() const { return x; }
    explicit constexpr operator int() const { return x; }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(getMod() - x);
        return res;
    }
    constexpr MInt inv() const {
        assert(x != 0);
        return ksm(*this, getMod() - 2);
    }
    constexpr MInt &operator*=(MInt rhs) & {
        x = 1LL * x * rhs.x % getMod();
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) & {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) & {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) & { return *this *= rhs.inv(); }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr istream &operator>>(istream &is, MInt &a) {
        ll v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr ostream &operator<<(ostream &os, const MInt &a) { return os << a.val(); }
    friend constexpr bool operator==(MInt lhs, MInt rhs) { return lhs.val() == rhs.val(); }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) { return lhs.val() != rhs.val(); }
};

template <>
int MInt<0>::Mod = 998244353;

template <int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();

using Z = MInt<MOD>;