Codeforces Round 917 (Div. 2)

https://codeforces.com/contest/1917

A. Least Product *800

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

讨论一下负数的个数和 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 还行的思维题

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

删除后的串长这样(剩下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,无限循环数组 bb=[v1,v2,,vk,v1,v2,,vk,])。

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

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

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

n2000,k105,kd109,1vin

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

首次把数组清零后,不管怎么操作数组中的数都单调不增的,所以 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 挺好的题

给定 12n1 中所有奇数的排列 p[]0k1 的排列 q[],数组 a[] 定义为:aik+j=pi2qj,求 a[] 的逆序对数。

n,k2e5

p2q0,p2q1,,p2qk 的逆序对数就是排列 q[] 的逆序对数,下文不考虑这种情况。

对于一个数 p2q,考虑位置在它之后的哪些数小于它:

  • x20,x21,,x2q1 ,x(p,2p)
  • x20,x21,,x2q2 ,x(2p,4p)
  • x20,x21,,x2q3 ,x(4p,8p)

同样还有

  • x20,x21,,x2q+1 ,x(p/2,p)

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

  • p21x20 形成 1 个逆序对
  • p22x20,x21 形成 2 个逆序对

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

  • 形如 {p2q,x2q} 的逆序对,x(0,p)qk 种取值
  • 形如 {p2q,x2q1} 的逆序对,x(0,2p)qk1 种取值
  • 形如 {p2q,x2q+1} 的逆序对,x(0,p/2)qk1 种取值

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

#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 矩阵,满足:共有 k1、每行异或和相等、每列异或和相等。

若无法构造,输出 No

0kn2

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

只有两个 1 或只有两个 0

  • k=2k=n22,仅当 n=2 时有解

其他情况,

  • k=0,4,8,12,,在任意空白处不断填 2×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,,先把上面的矩阵取反,

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

    然后在旁边加 2×2 的全 1 矩阵,即可扩展到 nk 的任意偶数的情况,

    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

n2000,d2000

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

所有边按从小到大排序,记为 l1,l2,,ln

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

image

如果 ln 与某条红边构成直径则无解。实际上,若 ln+ln1>d 则无解,因为肯定存在过他们俩的边,使直径 >d

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

image

背包,g[i][j][k]=True/False 表示在前 i 条边中选,蓝边的边权和为 j,绿边的边权和为 k 是否合法。复杂度 O(nd2)。绝望之中想起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 @   Bellala  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2022-02-04 cf730 J. Bottles(01背包变式)
2022-02-04 cf597 C. Subsequences(dp+树状数组)
2022-02-04 cf811 C. Vladik and Memorable Trip(dp)
2022-02-04 cf883 I. Photo Processing(二分答案+dp+双指针)
2022-02-04 cf750D. New Year and Fireworks(dfs)
2022-02-04 cf232 B. Table(dp)
2022-02-04 cf510 D. Fox And Jumping(dp)
点击右上角即可分享
微信分享提示