CF Edu178 ABCDE

CF Edu178 ABCDE

题目经过dpsk小姐翻译

过了5题还只能排2000+,无语了

目录

A

题目名称:三堆卡牌游戏

喵喵~Monocarp在桌子上放了3堆卡牌喵!第一堆有a张牌,第二堆有b张牌,第三堆有c张牌,而且满足a < b < c这个条件喵~

现在Monocarp想从第三堆牌中拿走一些牌(至少要拿1张,最多可以拿c张),然后把这些牌分给前两堆喵!每张拿走的牌都要分到第一堆或者第二堆里去喵当然也可以把所有拿的牌都分到同一堆里喵

你的任务是判断Monocarp能不能通过这样的操作,让三堆牌的数量变得一样多喵!

喵~简单来说就是:

  1. 从最多的第三堆拿牌
  2. 把拿的牌分给前两堆
  3. 看看能不能让三堆牌数变得相同

要特别注意a < b < c这个条件喵!


已知:

\[ a<b<c \\ a+x=b+y \\ a+x = c-x-y \\ \]

则得到:

\[y = \frac{c - 2b + a}{3} \\ \]

\[(c - 2b + a) \% 3 == 0 \\ (c - 2b + a) \geq 0 \\ \]

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol()
{
    ll a, b, c;
    cin >> a >> b >> c;
    if ((c + a - 2 * b) % 3 != 0)
    {
        cout << "NO" << endl;
        return;
    }
    if ((c + a - 2 * b) < 0)
    {
        cout << "NO" << endl;
        return;
    }
    cout << "YES\n";
}

int main()
{
    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}

B

给定一个由n个整数组成的数组a。

对于从1到n的每一个整数k,你需要执行以下操作:

  1. 任意选择数组a中的一个元素,将它移动到数组的最右端(如果选择最后一个元素,数组不会发生变化);
  2. 输出数组a最后k个元素的和;
  3. 将第一步移动的元素放回它原来的位置(恢复原始数组a)。

对于每一个k,你需要选择移动的元素,使得输出的值尽可能大。

请计算并输出每一个k对应的最大值。

喵~简单来说就是:
每次可以选一个数放到最后,计算最后k个数的和(要尽可能大),然后把这个数放回原位。需要为每个k=1到n都算出这个最大和哦!(ฅ´ω`ฅ)


n轮相互独立, 所以每轮单独考虑

对于每一轮:

    • 在后缀和前面选一个最大的数放到最后,挤掉了第k个数
  1. 不换
    • 直接后缀和

两种情况取最大值即可

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol()
{

    ll n;
    cin >> n;
    vector<ll> a(n + 2, 0), maxx(n + 2, 0), pre(n + 2, 0);
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    for (int i = 1; i <= n; i++)
    {
        maxx[i] = max(maxx[i - 1], a[i]);
        pre[i] = pre[i - 1] + a[i];
    }

    for (int i = 1; i <= n; ++i)
    {
        ll pref = pre[n] - pre[n - i];
        ll modify=max(0ll,maxx[n - i] - a[n - i + 1]);

        cout << pref + modify<< ' ';
    }
    cout << endl;
}

int main()
{
    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}

C

爱丽丝和鲍勃正在玩一个游戏喵~他们有一堆编号从1到n的卡牌。游戏开始时,这些卡牌会分别发给爱丽丝和鲍勃。

【卡牌胜负规则】

  • 通常来说,数字大的牌能打败数字小的牌喵~(i > j)
  • 但是有个特例喵!1号牌可以打败n号牌哦!(`・ω・´)

【游戏流程】

  1. 每回合开始时,双方必须至少有一张牌才能继续游戏喵~
  2. 爱丽丝先出牌,从她的牌堆中选一张放到桌上
  3. 鲍勃看到爱丽丝出的牌后,也选一张自己的牌放到桌上
  4. 比较胜负:
    • 如果爱丽丝的牌赢了,两张牌都归爱丽丝所有喵~
    • 否则两张牌都归鲍勃所有
  5. 赢得的牌可以在之后的回合中使用哦!

【游戏结束】
当某一方在回合开始时没有牌了,就输掉游戏啦~

题目要求:在双方都采取最优策略的情况下,判断谁会获胜喵!


1 号牌只能打败n号牌
因此我们可以考虑分类讨论1,n号牌给了谁

先特判一张牌的情况

之后只用考虑两个人都至少有两张牌的情况

  • \(1 , n\) 在同一个人手上

    • 显然这个人只需要不停出n就赢了
  • \(1 , n\) 在不同人手上

    • Alice 有 \(1\)
      • Alice 无论如何先手不能出1
      • Bob出n就赢了
    • Alice 有 \(n\)
      • Alice 先手不能出\(n\),他会出剩余的最大值
      • Bob必须要比这个值大才能赢
      • 所以看谁有\(n-1\)就行

代码写得有点丑

#include <bits/stdc++.h>
 
using namespace std;
using ll = long long;
 
using namespace std;
void sol()
{
 
    int n;
    cin >> n;
    vector<int> AAA, BBB;
    string s;
    cin >> s;
    int cnt = 1;
    for (auto i : s)
    {
        if (i == 'A')
            AAA.push_back(cnt);
        else
            BBB.push_back(cnt);
        ++cnt;
    }
    if (AAA.size() == 1 and BBB.size() == 1)
    {
        if (AAA[0] < BBB[0])
            cout << "Alice" << endl;
        else
            cout << "Bob" << endl;
        return;
    }
 
    if (AAA.size() == 1)
    {
        cout << "Bob" << endl;
        return;
    }
    if (BBB.size() == 1)
    {
        cout << "Alice" << endl;
        return;
    }
 
    if (AAA[0] == 1 and AAA[AAA.size() - 1] == n)
    {
        cout << "Alice" << endl;
        return;
    }
    if (BBB[0] == 1 and BBB[BBB.size() - 1] == n)
    {
        cout << "Bob" << endl;
        return;
    }
 
    if (AAA[0] == 1)
    {
        cout << "Bob" << endl;
        return;
    }
    else
    {
        if (AAA[AAA.size() - 2] > BBB[BBB.size() - 1])
        {
            cout << "Alice" << endl;
            return;
        }
        else
        {
            cout << "Bob" << endl;
            return;
        }
    }
}
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
 
    int t;
    cin >> t;
    while (t--)
        sol();
    return 0;
}


D

喵呜~让我来帮你翻译这道算法题目吧!(≧▽≦)

题目名称:打造完美数组喵~

题目描述: 给你一个大小为n的整数数组a喵~

你可以进行以下操作任意次数(包括零次):

花费1枚硬币,将数组中任意一个元素增加1(执行此操作时至少要有1枚硬币喵~)
获得1枚硬币,将数组中任意一个元素减少1
我们称一个数组是"完美数组"当且仅当满足以下两个条件:

  • 数组中的每个元素都至少为2喵~
  • 对于数组中任意两个不同的元素a_i和a_j,它们的最大公约数(GCD)等于1。如果数组中元素少于2个,这个条件自动满足喵~

我们称一个数组是"美丽数组"如果可以通过上述操作(初始硬币数为0)将其变成完美数组。如果数组已经是完美数组,那它也是美丽数组喵~

给定的数组不一定是美丽或完美的。你可以从中删除任意数量的元素(可以全部删除或者一个都不删)。你的任务是计算最少需要删除多少个元素(可以是零个)才能使数组变得美丽喵~


最少需要删除多少个元素

转化为

最多可以保留多少个元素

这样可以贪心地做了

\(+/-\)操作可以任意次,\(-\)操作 \(\geq\) \(+\)操作
所以我们可以把所有的数变成任意样子,只要总和 \(\leq sum_{原}\) 就行

又因为要求任意两数 \(gcd=1\),且 大于 \(1\)
可以构造$2,3,5,7,11, ... $ 这样全是素数的序列,都求前缀和然后遍历找就行,也可以二分(

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

using namespace std;
const ll MAX_PRIME_COUNT = 400005;

const ll SIEVE_LIMIT = 6500005;

vector<bool> primesss(SIEVE_LIMIT + 1, 1);// 素数筛

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

// ------预处理素数----------------- 
    primesss[0] = primesss[1] = false;
    for (int i = 2; i * i <= SIEVE_LIMIT; i++)
        if (primesss[i])
            for (int j = i * i; j <= SIEVE_LIMIT; j += i)
                primesss[j] = false;
    vector<ll> primes;//存储素数
    primes.reserve(MAX_PRIME_COUNT);
    for (int i = 2; i <= SIEVE_LIMIT && (int)primes.size() < MAX_PRIME_COUNT; i++)
    {
        if (primesss[i])
            primes.push_back(i);
    }
    vector<ll> f(primes.size() + 1, 0ll);//素数前缀和
    for (size_t i = 0; i < primes.size(); i++)
        f[i + 1] = f[i] + primes[i];

// ----------------------------------

    ll t;
    cin >> t;
    while (t--)
    {
        ll n;
        cin >> n;
        ll mmx = 0;
        vector<ll> arr(n), pref(n + 1, 0ll);

        for (int i = 0; i < n; i++)
            cin >> arr[i];

        sort(arr.begin(), arr.end(), greater<ll>());

        for (int i = 0; i < n; i++)
            pref[i + 1] = pref[i] + arr[i];

        for (int m = 1; m <= n; m++)
        {
            if (pref[m] >= f[m])
                mmx = m;
        }
        cout << (n - mmx) << endl;
    }
    return 0;
}

E

题目描述:
我们把一个字母称为"允许字母",如果它是小写字母并且是拉丁字母表前k个字母中的一个喵~

现在给你一个长度为n的字符串s,它只由"允许字母"组成喵~

如果一个字符串t是s的子序列,我们就称t是"可爱的"字符串喵~

接下来会给出q个字符串t₁, t₂, ..., tq,它们也都只由"允许字母"组成喵对于每个字符串ti,你需要计算出最少需要在它的右边添加多少个"允许字母",才能让它变得"不可爱"(即不再是s的子序列)喵

补充说明:
字符串t是字符串s的子序列,是指可以通过从s中删除零个或多个字符(可以从任意位置删除)得到t喵~


一看q个字符串,
就想到要预处理一下s了,刚好看到过字符串自动机

有时间更新一下

#include <bits/stdc++.h>
 
using namespace std;
using ll = long long;
 
using namespace std;
 
void sol()
{
 
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    
    const ll INFFER = 1145141919;
    vector<array<ll, 26>> f(n + 1);//状态机
/*
预处理 s,让 f[i][c] 表示:从 s 中位置 i(0‐索引)开始,遇到字母 c 的第一个位置。如果没有出现,则无穷大


*/
    for (int c = 0; c < 26; c++)
        f[n][c] = INFFER;

    for (int i = n - 1; i >= 0; i--)
    {
        f[i] = f[i + 1];
        int lc = s[i] - 'a';
        f[i][lc] = i;
    }
 
    vector<int> dp(n + 1, 0);
 
    dp[n] = 1;
    for (int i = n - 1; i >= 0; i--)
    {
        int best = INT_MAX;
        for (int c = 0; c < k; c++)
        {
            int nxt = f[i][c];
            int maybe;
            if (nxt == INFFER)
                maybe = 1;
            else
                maybe = 1 + dp[nxt + 1];
 
            best = min(best, maybe);
        }
        dp[i] = best;
    }
 
    int q;
    cin >> q;
    while (q--)
    {
        string t;
        cin >> t;
        int cur = 0;
        bool notok = 0;
        for (char ch : t)
        {
            int lc = ch - 'a';
 
            int nxt = f[cur][lc];
            if (nxt == INFFER)
            {
                notok = true;
                break;
            }
            else
            {
                cur = nxt + 1;
                if (cur > n)
                    cur = n;
            }
        }
        if (notok)
            cout << 0 << endl;
        else
            cout << dp[cur] << endl;
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    sol();
 
    return 0;
}
posted @ 2025-04-29 10:36  aminuosi  阅读(22)  评论(0)    收藏  举报