Educational Codeforces Round 161 (Rated for Div. 2)

https://codeforces.com/contest/1922

相当无聊的一场。E题勉强有一丁点意思,F题还行。

A. Tricky Template *800

对包含大小写字母的模式串 \(t\),称只包含小写字母的串 \(s\)\(t\) 匹配,当且仅当对于每个 \(i\),若 \(t_i\) 为小写则 \(s_i=t_i\),若 \(t_i\) 为大写则 \(s_i\neq t_i\)

给定三个小写串 \(a,b,c\),问是否存在模式串使得 \(a,b\) 匹配且 \(c\) 不匹配。

分类讨论一下即可。

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

string sol() {
    int n; string a, b, c;
    cin >> n >> a >> b >> c;
    for (int i = 0; i < n; i++) {
        if (a[i] == b[i] && a[i] != c[i]) return "YES";
        if (a[i] != b[i] && a[i] != c[i] && b[i] != c[i]) return "YES";
    }
    return "NO";
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T; cin >> T; while (T--)
        cout << sol() << '\n';
}

B. Forming Triangles *1200 入门计数题

给定 \(n\) 根木棒的长度,第 \(n\) 根木棒的长度是 \(2^{a_i}\)。问能组成的非退化三角形的个数。

\(n\le 3e5\)

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

void sol() {
    int n;
    cin >> n;
    vector<ll> c(n + 1); //桶
    for (int i = 0; i < n; i++) {
        int x; cin >> x; c[x]++;
    }
    ll ans = 0;
    for (int i = 0, s = 0; i <= n; i++) {
        if (c[i] > 0) ans += c[i] * (c[i] - 1) / 2 * s; 
        if (c[i] > 1) ans += c[i] * (c[i] - 1) * (c[i] - 2) / 6;
        s += c[i];
    }
    cout << ans << '\n';
}

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

C. Closest Cities *1300 语法题

给定数轴上 \(n\) 个城市的位置,每个城市的最近城市唯一。走到最近城市的时间是 \(1\),走到非最近城市的时间是距离,\(m\) 次询问从一地走到另一地的时间。

模拟。

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

void sol() {
    int n, m;
    cin >> n;
    vector<ll> a(n + 2), f(n + 1), g(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    
    a[0] = -1e12, a[n + 1] = 1e12;
    for (int i = 2; i <= n; i++) //从左到右走
        f[i] = f[i - 1] + (a[i] - a[i - 1] < a[i - 1] - a[i - 2] ? 1 : a[i] - a[i - 1]);
    for (int i = n - 1; i; i--) //从右到左走
        g[i] = g[i + 1] + (a[i + 1] - a[i] < a[i + 2] - a[i + 1] ? 1 : a[i + 1] - a[i]);
    
    cin >> m;
    while (m--) {
        int x, y;
        cin >> x >> y;
        if (x < y) cout << f[y] - f[x] << '\n';
        else cout << g[y] - g[x] << '\n';
    }
}

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

D. Berserk Monsters *1900 链表模拟题

每个怪有攻击和防御,每个回合每个怪受相邻(一个或两个)怪的攻击,受的总攻击 \(>\) 自己的防御则死,每回合后活着的怪重新紧凑分布。问每回合死几只怪。

\(n\le 3e5\)

如果一个怪的邻居本回合都没死,他自己也没死,那他下一回合也不会死,不用考虑他。链表模拟,vector套vector记录每回合死了谁,每回合只用考虑上一回合死的怪的邻居。

很无聊,好像适合出成CSP-J组题

复杂度 \(O(n)\),别老想着set

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

void sol() {
    int n;
    cin >> n;
    vector<int> a(n + 1), d(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> d[i];
    
    vector<int> l(n + 1), r(n + 1);
    for (int i = 1; i <= n; i++) {
        l[i] = i - 1;
        r[i] = i + 1;
    }
    r[n] = 0;
    
    vector<bool> died(n + 1);
    vector<vector<int>> ve(n + 1); //每次死的怪
    died[0] = true;
    for (int i = 1; i <= n; i++) ve[0].push_back(i);
    
    auto check = [&](int p) { //检查p本轮能不能死
        if (!died[p] && a[l[p]] + a[r[p]] > d[p]) {
            died[p] = true;
            return true;
        }
        return false;
    };
    
    for (int i = 1; i <= n; i++) {
        for (int p : ve[i - 1]) {
            if (check(l[p])) ve[i].push_back(l[p]);
            if (check(r[p])) ve[i].push_back(r[p]);
        }
        for (int p : ve[i]) {
            l[r[p]] = l[p];
            r[l[p]] = r[p];
        }
    }
    
    for (int i = 1; i <= n; i++)
        cout << ve[i].size() << ' ';
    cout << '\n';
}

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

E. Increasing Subsequences *1800 简单构造

构造长度不超过 \(200\) 且上升子序列数为 \(x\) 的数列 \(a[]\)

\(x\le 1e18\),空序列也算入。

注意到严格递增数列的上升子列数为 \(2^n\),考虑二进制。

从高到低考虑 \(x\) 的二进制位,若 \(x\)\(i\) 这个位则把一个长度为 \(i\) 且最大值小于之前任何数的严格递增数列接到答案之尾。

这样长度会超过 \(200\)。考虑对前面的严格递增子列进行复用,对于 \(x\) 的每个非最高位只需新增一个数。

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

void sol() {
    ll x; cin >> x;
    vector<int> a;
    
    int w = __lg(x);
    for (int i = 1; i <= w; i++)
        a.push_back(2 * i - 1);
    x -= 1ll << w;
    
    while (x) {
        int w = __lg(x);
        a.push_back(w * 2);
        x -= 1ll << w;
    }
    
    cout << a.size() << '\n';
    for (auto i : a) cout << i << ' ';
    cout << '\n';
}

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

F. Replace on Segment *2500 区间dp

给定整数数组 \(a[]\)\(a_i\in [1,x]\)。每次可选一区间,把区间中的数都变成一个区间中没有的数(仍要在 \([1,x]\) 内),问使所有数相等的最小操作次数。

\(1\le x\le n\le 100\)

(开始想到贪心选择把数组划分成的非空区间数量最少的数,被样例3 hack了。)

显然区间dp。\(f(l,r,num)\) 把区间中的所有数变成 \(num\) 的最小操作次数,(这题的独特之处是)还要开个 \(g(l,r,num)\) 表示把区间中的所有数变成都不是 \(num\) 的最小操作次数。

注意 \(g\) 数组的更新有两种方式,一种是把区间全变成某个不为 \(num\) 的数(让区间中的所有数相等),第二种是左右子区间中没有 \(num\) 就行,但不要求区间中的数都相等。

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

void sol() {
    int n, x;
    cin >> n >> x;
    
    vector f(n + 1, vector(n + 1, vector<int>(x + 1, 1e9)));
    vector g(n + 1, vector(n + 1, vector<int>(x + 1, 0)));
    
    for (int i = 1; i <= n; i++) {
        int a; cin >> a;
        fill(f[i][i].begin(), f[i][i].end(), 1);
        f[i][i][a] = 0;
        g[i][i][a] = 1;
    }
    
    for (int len = 2; len <= n; len++) {
        for (int l = 1, r = len; r <= n; l++, r++) {
            //更新f
            for (int k = l; k < r; k++) {
                for (int a = 1; a <= x; a++) {
                    f[l][r][a] = min({f[l][r][a],
                        f[l][k][a] + f[k + 1][r][a],
                        g[l][k][a] + g[k + 1][r][a] + 1});
                }
            }
            //第一种得到g的方法
            for (int a = 1, premin = 1e9; a <= x; a++) {
                g[l][r][a] = premin;
                premin = min(premin, f[l][r][a]);
            }
            for (int a = x, sufmin = 1e9; a >= 1; a--) {
                g[l][r][a] = min(g[l][r][a], sufmin);
                sufmin = min(sufmin, f[l][r][a]);
            }
            //第二种得到g的方法
            for (int k = l; k < r; k++) {
                for (int a = 1; a <= x; a++) {
                    g[l][r][a] = min(g[l][r][a], g[l][k][a] + g[k + 1][r][a]);
                }
            }
        }
    }
    cout << *min_element(f[1][n].begin(), f[1][n].end()) << '\n';
}

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