2026.6.27 Codeforces Round 1105 (Div. 2)

比赛链接


A. Another Popcount Problem

题意

给定 \(n, k\),构造一个长度为 \(k\) 序列,和为 \(n\),二进制位数和最大。

题解

贪心,从低位到高位构造。

void solve()
{
    ll n, m; cin >> m >> n;
    if (m <= n) cout << m << '\n';
    else 
    {
        ll ans = 0;
        for (int k=0; k<31; ++k)
        {
            if (m >= (n<<k)) m -= n<<k, ans += n;
            else
            {
                ans += m>>k; break;
            }
        }
        cout << ans << '\n';
    }
}

B. AI Finds Nothing Here

题意

\(n\times m\) 的 01 矩阵为好矩阵如果它的所有 \(r\times c\) 子矩阵异或和都为 \(0\)。给定 \(n,m,r,c\) 求好矩阵数量,对 \(998244353\) 取模。

题解

只要确定左上角的 \(r-1\) 行和 \(c-1\) 列就能递推确定剩下的元素。所以答案是 \(2^{nm - (n-r+1)(m-c+1)}\)

void solve()
{
    int n, m, r, c; cin >> n >> m >> r >> c;
    cout << qpow(2, 1ll * n * m - 1ll * (n-r+1) * (m-c+1)) << '\n';
}

C. Nim Game Is XOR Game

题意

\(n\) 堆石子,两人博弈,每个人可以取走数量异或和为 \(0\) 的石子(不能不取),无法操作者输,求先手必胜方案数。

题解

没有石子或只有一堆非空石子是必败的;

在其他任何情况下,都可以取空 \(n-1\) 堆石子再从最后一堆石子中取一部分来达到上面的必败态。

void solve()
{
    int n; cin >> n;
    vector <int> a(n);
    int s = 0;
    for (int i=0; i<n; ++i) cin >> a[i], s ^= a[i];
    if (n == 1) { cout << "0\n"; return; }
    if (!s) { cout << "1\n"; return; }
    int ans = 0;
    for (int i=0; i<n; ++i)
    {
        if (a[i] > (s^a[i])) ++ans;
    }
    cout << ans << '\n';
}

D. Decidophobia

题意

\(n\) 个人围成一圈,每个人有一个权值 \(a_i\)。现要给其中一些人发礼物,分发后,每个人有一个分数:
若他收到了礼物,则分数为左右 \(2d\) 个人中没有礼物的人数乘 \(a_i\);若他没收到礼物,则分数为左右 \(2d\) 个人中有礼物的人数乘 \(-a_i\)
求所有人分数之和的最大值。

题解

先假设所有人都没发礼物,则给一个人礼物对总分的贡献是 \(2d\times a_i\) 减去周围 \(2d\) 个人的权值。

void solve()
{
    int n, d; cin >> n >> d;
    vector <ll> a(n*3+1), s(n*3+1);
    for (int i=1; i<=n; ++i) cin >> a[i], a[i+n] = a[i+2*n] = a[i];
    for (int i=1; i<=n*3; ++i) s[i] = s[i-1] + a[i];
    ll ans = 0;
    for (int i=1; i<=n; ++i)
    {
        ll w = a[i] * d * 2 - (s[i+n+d] - s[i+n]) - (s[i+n-1] - s[i+n-d-1]);
        ans += max(w, 0ll);
    }
    cout << ans << '\n';
}

E. Revival

题意

对一个排列 \(p\),定义 \(s_i\)\(p\) 的第 \(i\) 个前缀的逆序对数。现对每个位置 \(i\) 给出 \(p_i\)\(s_i\) 之一,还原排列 \(p\)。保证解一定存在。

题解

从后往前依次确定每个 \(s_i\) 对应的 \(p_i\)

假设当前最后一个未确定的位置为 \(r\),倒数第二个未确定的位置为 \(l\)。我们已知 \(s_r, s_l\) 和当前未填入排列的数字集合 \(S\)

\(s_r - s_l\) 就是 \(a_{l+1, \dots, r-1}\) 的贡献(可直接计算)和 \(a_r := x\) 的贡献。将未知项拆分出来就是

\[s_r - s_l - \sum_{i=l+1}^{r-1} V(a_i, S \backslash \{a_{l+1, \dots, i-1}\}) + (r-l-1) = V(x, S) + V(x, a_{l+1, \dots, r-1}) \]

其中 \(V(x, S) = \sum_{y\in S} [y>x]\)

用树状数组维护逆序对即可。\(x\) 可以直接二分。复杂度 \(O(n\log^2 n)\)(其实也可以 \(O(n\log n)\)

struct bit
{
    int n;
    vector <int> c;

    bit(int n) : n(n) { c.resize(n+1); }

    void upd(int x, int v=1)
    {
        for (; x; x-=x&-x) c[x] += v;
    }

    int qry(int x)
    {
        int r = 0;
        for (; x<=n; x+=x&-x) r += c[x];
        return r;
    }
};

void solve()
{
    int n; cin >> n;
    vector <char> c(n+1);
    vector <ll> a(n+1);
    vector <int> ans(n+1);

    for (int i=1; i<=n; ++i)
    {
        string _;
        cin >> _ >> a[i];
        c[i] = _[0];
    }

    bit p(n), q(n);
    for (int i=1; i<=n; ++i) p.upd(i, 1), q.upd(i, 1);

    int l, r = n;
    while (c[r] == 'p')
    {
        p.upd(a[r], -1), q.upd(a[r], -1);
        ans[r] = a[r];
        --r;
    }

    while (r>0)
    {
        l = r-1;
        while (l>0 && c[l] == 'p') --l;
        ll val = a[r] - a[l] + (r-l-1);
        for (int i=r-1; i>l; --i)
        {
            val -= p.qry(a[i]+1);
            p.upd(a[i], -1);
            q.upd(a[i], 1);
            ans[i] = a[i];
        }
        int L = 1, R = n, mid, res;
        while (L <= R)
        {
            mid = (L+R) >> 1;
            if (q.qry(mid+1) <= val) res = mid, R = mid-1;
            else L = mid+1;
        }
        p.upd(res, -1), q.upd(res, -1);
        for (int i=r-1; i>l; --i) q.upd(a[i], -2);
        ans[r] = res; r = l;
    }
    for (int i=1; i<=n; ++i) cout << ans[i] << ' ';
    cout << '\n';
}
posted @ 2026-06-29 17:25  EssnSlaryt  阅读(53)  评论(0)    收藏  举报