跳转至

Educational Codeforces Round 150 (Rated for Div. 2)

比赛链接: https://codeforces.com/contest/1841

2025-03-19

VP 赛时只过了AB,C题花了好长时间才弄出来。

学习一下C的简单的写法,然后看看DE。试下能不能尽量把人数大于2000的题都学习一下。。

A

一定要读好题目啊,我A错了一发。。

急了,题都没读明白就交了。

void ChatGptDeepSeek()
{
    int n;
    cin>>n;
    if(n<=4) cout<<"Bob\n";
    else cout<<"Alice\n";
}

B

没啥说的,虽然代码一开始写也是错的。。cin>>x[i] 写成了 cin>>x[q] ,就很搞笑了,找了半天。。

void ChatGptDeepSeek()
{
    int q;
    cin >> q;
    vector<int> x(q);
    for (int i = 0; i < q; i++)
        cin >> x[i];
    int lst = 0;
    bool ok = true;
    for (int i = 0; i < q; i++) {
        // cerr << "lst:" << lst << " \n";
        if (!ok) {
            if (x[i] <= x[0] && x[i] >= lst) {
                cout << "1";
                lst = x[i];
            } else
                cout << "0";
            continue;
        }
        if (x[i] >= lst) {
            cout << "1";
            lst = x[i];
        } else if (ok && x[i] <= x[0]) {
            cout << "1";
            ok = false;
            lst = x[i];
        } else
            cout << "0";
    }
    cout << '\n';
}

C

我的思路

我们可以枚举把每一个位置改成每一种字符的答案,首先我们计算去掉这个位置时的答案。肯定去掉自己本身的贡献。

但是可能去掉这一位,前面有的位的贡献会从负变为正。这种情况只出现在暂时没有遇到比自己大的位的位上,且去掉的这个数字必须是后缀的唯一最大值。如果存在这种情况,则需更新当前的后缀最大值。

然后可以计算当前这一位替换为其他字符的贡献。如果改的比当前的后缀最大值小,或者相等,那么不会影响前面的答案,只需增加当前位的贡献。

若更新得更大了,假设更新为 \(j\) ,则前面的在 \([suf_{now},j-1]\) 的位的贡献会由正变成负。

然后就模拟就行了。。

void ChatGptDeepSeek()
{
    string s;
    cin >> s;
    s = " " + s;
    int n = s.size() - 1;
    vector<vector<int>> pre(n + 1, vector<int>(5));
    for (int i = 1; i <= n; i++) {
        pre[i] = pre[i - 1];
        pre[i][s[i] - 'A']++;
    }
    vector<int> suf(n + 1);
    suf[n] = s[n] - 'A';
    for (int i = n - 1; i >= 1; i--) {
        suf[i] = max(suf[i + 1], s[i] - 'A');
    }
    vector<int> val { 1, 10, 100, 1000, 10000 };
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        if (s[i] - 'A' >= suf[i])
            ans += val[s[i] - 'A'];
        else
            ans -= val[s[i] - 'A'];
    }
    ll res = ans;
    vector<int> cnt(5); // 每一种会被影响的 有多少个
    for (int i = 1; i <= n; i++) {
        ll now = ans;
        if (s[i] - 'A' == suf[i])
            now -= val[s[i] - 'A'];
        else
            now += val[s[i] - 'A'];
        int now_suf = suf[i];
        if (s[i] - 'A' == suf[i]) {
            if (pre[n][s[i] - 'A'] - pre[i - 1][s[i] - 'A'] == 1) {
                int sec = 0;
                for (int j = s[i] - 'A' - 1; j >= 0; j--) {
                    if (pre[n][j] - pre[i - 1][j]) {
                        sec = j;
                        break;
                    }
                }
                now_suf = sec;
                for (int j = sec; j < suf[i]; j++) {
                    now += cnt[j] * 2 * val[j];
                }
            }
        }
        for (int j = 0; j < 5; j++) {
            if (j == s[i] - 'A')
                continue;
            if (j == now_suf) {
                res = max(res, now + val[j]);
            } else if (j > now_suf) {
                // now_suf,j-1 之间的 ,减去
                ll sub = 0;
                for (int k = now_suf; k < j; k++) {
                    sub += 2LL * cnt[k] * val[k];
                }
                res = max(res, now + val[j] - sub);
            } else if (j < now_suf) {
                res = max(res, now - val[j]);
            }
        }
        for (int j = 0; j < s[i] - 'A'; j++)
            cnt[j] = 0;
        cnt[s[i] - 'A']++;
    }
    cout << res << '\n';
}

DP

翻转字符串。。这样就是看前面有没有更大的字符了。。会简单很多。

好好好,看了题解就很爽了。。

\(dp_{0/1,i}\) 表示当前是否修改,最大值是 \(i\) ,只需要先翻转字符串,就变成了需要看前面的最大的字符。

constexpr int val[] { 1, 10, 100, 1000, 10000 };
void ChatGptDeepSeek()
{
    string s;
    cin >> s;
    vector<vector<int>> dp(2, vector<int>(5, -1e9 + 1));
    dp[0][0] = 0;
    reverse(s.begin(), s.end());
    for (int i = 0; i < s.size(); i++) {
        vector ndp = vector<vector<int>>(2, vector<int>(5, -1e9 + 1));
        int x = s[i] - 'A';
        for (int j = 0; j < 5; j++) {
            for (int k = 0; k < 5; k++) {
                ndp[1][max(k, j)] = max(ndp[1][max(k, j)], dp[0][j] + (k < j ? -1 : 1) * val[k]);
            }
            ndp[0][max(x, j)] = max(ndp[0][max(x, j)], dp[0][j] + (x < j ? -1 : 1) * val[x]);
            ndp[1][max(x, j)] = max(ndp[1][max(x, j)], dp[1][j] + (x < j ? -1 : 1) * val[x]);
        }
        dp = ndp;
    }
    cout << max(*max_element(dp[0].begin(), dp[0].end()), *max_element(dp[1].begin(), dp[1].end())) << '\n';
}

D

感觉我这回写的DP很对啊。。。

题解很对,很简单,但我的哪里错了呢?

题解的思路是

先把所有相交的线段给合成一个线段,然后最后的答案其实就是从这里选若干个不相交的线段出来。所以可以按 r 排序,能拿就拿。

void ChatGptDeepSeek()
{
    int n;
    cin >> n;
    vector<int> l(n), r(n);
    for (int i = 0; i < n; i++)
        cin >> l[i] >> r[i];
    vector<pair<int, int>> res;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < i; j++) {
            if (l[i] > r[j] || l[j] > r[i])
                continue;
            res.push_back({ min(l[i], l[j]), max(r[i], r[j]) });
        }
    sort(res.begin(), res.end(), [](pair<int, int> x, pair<int, int> y) { return x.second < y.second; });
    int R = -1, ans = 0;
    for (auto [l, r] : res) {
        if (l > R) {
            ans++;
            R = r;
        }
    }
    cout << n - 2 * ans << '\n';
}

改对了。。虽然没啥用。。对拍大法好啊。

void ChatGptDeepSeek()
{
    int n;
    cin >> n;
    vector<pair<int, int>> a(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> a[i].first >> a[i].second;
    sort(a.begin() + 1, a.end());
    vector<vector<int>> dp(n + 1, vector<int>(n + 1, 1e9 + 1));
    vector<int> ans(n + 1, 0);
    ans[0] = 0;
    dp[0][0] = -1;
    // dp[i][j]表示答案是j 的最小的 r
    for (int i = 1; i <= n; i++) {
        dp[i] = dp[i - 1];
        ans[i] = max(ans[i - 1], ans[i]);
        // if (a[i].second < a[i + 1].first)
        //     continue;
        /* 去前面找和自己相交的 */
        for (int j = 1; j < i; j++) {
            assert(ans[i] >= ans[j]);

            if (a[j].second < a[i].first)
                continue;
            for (int k = 0; k <= ans[j]; k++) {
                if (dp[j][k] < a[j].first) {
                    dp[i][k + 1] = min(dp[i][k + 1], max(a[i].second, a[j].second));
                    ans[i] = max(ans[i], k + 1);
                }
            }
        }

        for (int j = 1; j <= ans[i]; j++)
            assert(dp[i][j] <= dp[i][j + 1]);
    }
    cout << n - ans[n] * 2 << '\n';
    // cout << n << '\n';
}

又是两小时写一个题目。

E

六百六十六,我要直接抄题解了。。。抄完这题就先不看了。。

要求出每一个长度的段的数量。

.... 笑死了,抄题解都超不明白。。。还是早点休息吧。再也不想这样抄题解了。。

using ll = long long;
struct seg{
    int l, r;
};

bool operator <(const seg &a, const seg &b){
    return a.l < b.l;
}

void ChatGptDeepSeek()
{
    map<seg, int> mp;
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    ll m;
    cin >> m;
    mp[{ 0, n }] = n;
    vector<int> ord(n);
    iota(ord.begin(), ord.end(), 0);
    sort(ord.begin(), ord.end(), [&a](int x, int y) {
        return a[x] > a[y];
    });
    vector<ll> cnt(n + 2);
    int j = 0;
    for (int i = n; i >= 0; i--) {
        while (j < n && a[ord[j]] >= i) {
            auto it = mp.upper_bound({ ord[j], -1 });
            --it;
            auto tmp = it->first;

            cnt[tmp.r - tmp.l] += it->second - i;
            mp.erase(it);

            if (tmp.l != ord[j])
                mp[{ tmp.l, ord[j] }] = i;
            if (ord[j] + 1 != tmp.r)
                mp[{ ord[j] + 1, tmp.r }] = i;
            j++;
        }
    }
    ll ans = 0;
    for (int i = n; i > 0; --i) {
        long long t = min(cnt[i], m / i);
        ans += t * 1ll * (i - 1);
        m -= t * 1ll * i;
        if (t != cnt[i] && m > 0) {
            ans += m - 1;
            m = 0;
        }
    }
    cout << ans << '\n';
}