Codeforces Round 917 (Div. 2)

https://codeforces.com/contest/1917

A. Least Product *800

给定整数数组,可以把数组中的数 \(a_i\) 改为 \(0\sim a_i\) 中的任意整数,最小化所有数的乘积,在此基础上使操作次数最少

讨论一下负数的个数和 \(0\) 的个数

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void sol() {
    int n;
    cin >> n;
    bool zero = 0, neg = 0;
    while (n--) {
        int x; cin >> x;
        if (x < 0) neg ^= 1;
        if (x == 0) zero = true;
    }
    if (zero || neg) cout << "0\n";
    else cout << "1\n1 0\n";
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}

B. Erase First or Second Letter *1100 还行的思维题

给定字符串,可以删除首字符(即第一个字符)和删除第二个字符任意次,问能得到的本质不同串个数。 \(n\le 10^5\)

删除后的串长这样(剩下6个字符):xxxxxx??????

或者这样:xxxxx?xxxxxxxx?????

也就是说长度相同的串只可能在第一个字符处不同

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void sol() {
    int n;
    string s;
    cin >> n >> s;
    
    int ans = n;
    vector<int> cnt(26);
    for (char c : s) {
        for (int i = 0; i < 26; i++) {
            if (cnt[i] && c - 'a' != i) //前面的一个字符与当前字符不同
                ans++;
        }
        cnt[c - 'a']++;
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}

C. Watering an Array *1600 比较无聊

有一个长度为\(n\)的整数数组 \(a\),无限循环数组 \(b\)\(b=[v_1,v_2,\ldots,v_k,v_1,v_2,\ldots,v_k,\ldots]\))。

\(i\) 次操作你可以执行以下两种操作之一:

  • 将数组 \(a\) 的前 \(b_i\) 个元素的值都加 \(1\)
  • 计算使 \(a_j=j\) 成立的元素个数为 \(c\)。将 \(c\) 加到你的得分上,并将整个数组 \(a\) 的所有元素都置为 \(0\)

\(d\) 次操作后能获得的最大得分。

\(n\le 2000,k\le 10^5, k\le d\le 10^9, 1\le v_i\le n\)

我好像非常不擅长这种题。。。居然卡了很久

首次把数组清零后,不管怎么操作数组中的数都单调不增的,所以 \(c\) 至多为 \(1\)

所以首次把数组清零后,理想的方案是交替进行两种操作,每两次操作使答案 \(+1\)

枚举首次清零的时间即可。但是枚举到 \(n\) 是不够的!(wa了两发)

可以这样考虑:若交替进行两种操作,则答案增加的速度是 \(0.5\)。而如果在第 \(2n\) 次以后才进行首次操作二,最好的情况是使答案 \(+n\),答案增加的平均速度 \(<n/2n=0.5\),还不如早点开始交替。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 5;
ll n, k, d, a[N], v[N];
void sol() {
    cin >> n >> k >> d;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= k; i++) cin >> v[i % k];
    
    ll ans = 0;
    for (int i = 1; i <= min(d, n + n); i++) {
        ll res = 0;
        for (int j = 1; j <= n; j++) res += a[j] == j;
        ans = max(ans, res + (d - i) / 2);
        for (int j = 1; j <= v[i % k]; j++) a[j]++;
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}

D. Yet Another Inversions Problem *2300 挺好的题

给定 \(1\sim 2n-1\) 中所有奇数的排列 \(p[]\)\(0\sim k-1\) 的排列 \(q[]\),数组 \(a[]\) 定义为:\(a_{ik+j}=p_i\cdot 2^{q_j}\),求 \(a[]\) 的逆序对数。

\(n,k\le 2e5\)

\(p2^{q_0},p2^{q_1},\cdots ,p2^{q_k}\) 的逆序对数就是排列 \(q[]\) 的逆序对数,下文不考虑这种情况。

对于一个数 \(p2^{q}\),考虑位置在它之后的哪些数小于它:

  • \(x2^0,x2^1, \cdots, x2^{q-1}\) \(, x\in (p,2p)\)
  • \(x2^0,x2^1, \cdots, x2^{q-2}\) \(, x\in (2p,4p)\)
  • \(x2^0,x2^1, \cdots, x2^{q-3}\) \(, x\in (4p,8p)\)
  • \(\cdots\)

同样还有

  • \(x2^{0},x2^{1}, \cdots, x2^{q+1}\) \(, x\in (p/2,p)\)
  • \(\cdots\)

这样复杂度有点高。改为考虑某个 \(x\in (p,2p)\) 的贡献:

  • \(p2^{1}\)\(x2^0\) 形成 \(1\) 个逆序对
  • \(p2^{2}\)\(x2^0,x2^1\) 形成 \(2\) 个逆序对

这样完全可做,但可能不太好写。考虑枚举指数的差:

  • 形如 \(\{p2^q,x2^{q}\}\) 的逆序对,\(x\in(0,p)\)\(q\)\(k\) 种取值
  • 形如 \(\{p2^q,x2^{q-1}\}\) 的逆序对,\(x\in(0,2p)\)\(q\)\(k-1\) 种取值
  • 形如 \(\{p2^q,x2^{q+1}\}\) 的逆序对,\(x\in(0,p/2)\)\(q\)\(k-1\) 种取值

这样好像好写一点点。树状数组维护一下

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int P = 998244353;

struct BIT {
    int n; vector<int> tr;
    BIT(int n): n(n), tr(n + 1) {}
    int lowbit(int x) { return x & -x; }
    void add(int p, int x) { for (; p <= n; p += lowbit(p)) tr[p] += x; }
    int ask(int p) { int s = 0; for (; p > 0; p -= lowbit(p)) s += tr[p]; return s; }
};

void sol() {
    int n, k;
    cin >> n >> k;
    vector<int> p(n), q(k);
    for (int &x : p) cin >> x;
    for (int &x : q) cin >> x;

    ll ans = 0;
    
    //行内逆序对
    BIT row(k);
    for (int i = k - 1; ~i; i--) {
        ans += row.ask(q[i] + 1);
        row.add(q[i] + 1, 1);
    }
    (ans *= n) %= P;
    
    //行间逆序对
    BIT tr(2 * n - 1);
    for (int i = n - 1; ~i; i--) {
        for (ll _k = k, _p = p[i]; _k; _k--, _p *= 2) {
            if (_p >= 2 * n - 1) { //越界了,直接等差数列
                (ans += _k * (_k + 1) / 2 % P * tr.ask(2 * n - 1) % P) %= P;
                break;
            }
            (ans += _k * tr.ask(_p)) %= P;
        }
        for (ll _k = k - 1, _p = p[i] / 2; _k > 0 && _p > 0; _k--, _p /= 2)
            (ans += _k * tr.ask(_p)) %= P;
        tr.add(p[i], 1);
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}

E. Construct Matrix *2500 构造构造

给定偶数 \(n\) 和整数 \(k\),构造 \(01\) 矩阵,满足:共有 \(k\)\(1\)、每行异或和相等、每列异或和相等。

若无法构造,输出 No

\(0\le k \le n^2\)

因为每行异或和相等且 \(n\) 为偶数,故所有数异或和为 \(0\),所以 \(k\) 为奇数时无解。下面讨论 \(k\) 为偶数的情况:

只有两个 \(1\) 或只有两个 \(0\)

  • \(k=2\)\(k=n^2-2\),仅当 \(n=2\) 时有解

其他情况,

  • \(k=0,4,8,12,\cdots\),在任意空白处不断填 \(2\times 2\) 的全 \(1\) 矩阵,每行/列异或和始终是 \(0\)

  • \(k=6\)

    1 1 0 0
    1 0 1 0
    0 1 1 0
    0 0 0 0
    
  • $k=10,14,18,\cdots $,先把上面的矩阵取反,

    0 0 1 1
    0 1 0 1
    1 0 0 1
    1 1 1 1
    

    然后在旁边加 \(2\times 2\) 的全 \(1\) 矩阵,即可扩展到 \(n\)\(\ge k\) 的任意偶数的情况,

    0 0 1 1 1 1
    0 1 0 1 1 1
    1 0 0 1 1 1
    1 1 1 1 1 1
    1 1 1 1 1 1
    
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol() {
    int n, k;
    cin >> n >> k;    
    
    bool ok = true;
    vector<vector<int>> a(n + 1, vector<int>(n + 1));
    
    if (k % 2) {
        ok = false;
    } else if (k == 2 || n * n - k == 2) {
        if (n == 2) a[1][1] = a[2][2] = 1;
        else ok = false;
    } else if (k % 4 == 0) {
        for (int i = 1; i <= n; i += 2)
            for (int j = 1; j <= n && k; j += 2)
                a[i][j] = a[i + 1][j] = a[i][j + 1] = a[i + 1][j + 1] = 1, k -= 4;
    } else if (k == 6) {
        for (int i = 1; i <= 3; i++)
            for (int j = 1; j <= 3; j++)
                if (i + j != 4) a[i][j] = 1;
    } else {
        for (int i = 1; i <= 4; i++)
            for (int j = 1; j <= 4; j++)
                if (i + j == 4 || i == 4 || j == 4) a[i][j] = 1;
        k -= 10;
        for (int i = 1; i <= n; i += 2)
            for (int j = 1; j <= n && k; j += 2)
                if (i > 4 || j > 4)
                    a[i][j] = a[i + 1][j] = a[i][j + 1] = a[i + 1][j + 1] = 1, k -= 4;
    }
    
    if (!ok) cout << "No\n";
    else {
        cout << "Yes\n";
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                cout << a[i][j] << " \n"[j == n];
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}

F. Construct Tree *2500 bitset优化01背包

给定 \(n\) 条边的边权,用全部边构造一棵树,要求直径为 \(d\)

\(n\le 2000,d\le 2000\)

这种题目一般都是先弄出直径来,然后把其他边挂到某点上。

所有边按从小到大排序,记为 \(l_1,l_2,\cdots, l_n\)

如果能找出若干条边,边权之和为 \(d\),且包含 \(l_n\) 在内,那就把它们作为直径,\(l_n\) 放在一端,其他的边这样接:

image

如果 \(l_n\) 与某条红边构成直径则无解。实际上,若 \(l_n+l_{n-1}> d\) 则无解,因为肯定存在过他们俩的边,使直径 \(>d\)

否则如下图这样构造,绿边的边权和小于等于 \(l_n\),蓝边的边权和也小于等于 \(l_n\),且绿边的边权和加上蓝边的边权和等于 \(d\)

image

背包,\(g[i][j][k]=True/False\) 表示在前 \(i\) 条边中选,蓝边的边权和为 \(j\),绿边的边权和为 \(k\) 是否合法。复杂度 \(O(nd^2)\)。绝望之中想起bitset优化,复杂度除以 \(32\),能过。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2010;
void sol() {
    int n, d;
    cin >> n >> d;
    vector<int> l(n);
    for (int &x : l) cin >> x;
    sort(l.begin(), l.end());
    
    if (l[n - 1] + l[n - 2] > d) return cout << "No\n", void();
    
    bitset<N> f;
    f[0] = 1;
    for (int i = 0; i < n - 1; i++)
        f |= f << l[i];
    if (f[d - l[n - 1]]) return cout << "Yes\n", void();
    
    vector<bitset<N>> g(d + 1);
    g[0][0] = 1;
    for (int i = 0; i < n; i++) {
        for (int j = d; ~j; j--) {
            if (j >= l[i]) g[j] |= g[j - l[i]] | (g[j] << l[i]);
            else g[j] |= g[j] << l[i];
        }
    }
    for (int i = l[n - 1]; d - i >= l[n - 1]; i++) {
        if (g[i][d - i]) return cout << "Yes\n", void();
    }
    cout << "No\n";
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        sol();
}
posted @ 2024-02-04 22:34  Bellala  阅读(35)  评论(0编辑  收藏  举报