Educational Codeforces Round 152 (Rated for Div. 2) 赛后总结

小菜鸡的赛后总结


B.Monsters

  • 题意:有n个具有一定生命值的怪物,序号从1到n,每回合可以选择生命值最高的怪物造成k点伤害,如果有多个生命值最高的怪物,选择序号最小的那个,需要从最先死亡的怪物开始到最后死亡的依次输出它们的序号
  • 思路:通过模拟可以发现模上k后从大到小排序即可,而对于生命值为k倍数的,其值应为k
bool cmp(PII a, PII b)
{
    if (a.first == b.first)
    {
        return a.second < b.second;
    }
    return a.first > b.first;
}
 
void BlueFire()
{
    int n, k;
    cin >> n >> k;
    vector<PII> a(n);
    for (int i = 0; i < n; i++)
    {
        int t;
        cin >> t;
        t = (t - 1) % k + 1;
        a[i] = {t, i + 1};
    }
 
    sort(a.begin(), a.end(), cmp);
 
    for (int i = 0; i < n; i++)
        cout << a[i].second << " ";
    cout << endl;
    return;
}

C. Binary String Copying

  • 题意:给一个01字符串,要做m次拷贝,其中每次拷贝给出一个l和r,需要对这个字符串中的l到r位置的01序列进行排序,然后计算所有拷贝完成后有多少种不同的字符串
  • 思路:我们发现,l和r间有一部分是有序,有一部分是无序的,只需要将l和r都收缩到那一段无序的序列即可,而对这一段排序一定是一样的结果,所以收缩后记录这对l和r到set中自动去重即可,如何收缩,其实就是记录从l右边的第一个1,r左边的第一个0,看它们是否交叉,若交叉,则实际不需要排序,和原数组一样(这里我直接让l和r都变为-1),若不交叉,则需要排序,改变l和r记录即可,需要处理一些边界问题。
void BlueFire()
{
    int n, m;
    cin >> n >> m;
    string s;
    cin >> s;
    vector<int> left0(n);
    vector<int> right1(n);
    left0[0] = -1;
    right1[n - 1] = -1;
    for (int i = 0; i < n; i++)
    {
        if (i != 0)
            left0[i] = left0[i - 1];
        if (s[i] == '0')
            left0[i] = i;
    }

    for (int i = n - 1; i >= 0; i--)
    {
        if (i != n - 1)
            right1[i] = right1[i + 1];
        if (s[i] == '1')
            right1[i] = i;
    }
    set<PII> res;
    while (m--)
    {
        int l, r;
        cin >> l >> r;
        if (l == r)
            l = r = -1;
        else
        {
            l = right1[l - 1];
            r = left0[r - 1];
            if (l >= r || l == -1 || r == -1)
                l = r = -1;
        }

        PII t = {l, r};
        res.insert(t);
    }

    cout << res.size() << endl;

    return;
}

D. Array Painting

  • 题意:给出一个数组,每个数只能是0,1,2,假定最开始每个数都是蓝色的,可以执行两个操作
    1.花费一枚硬币,将一个数变成红色
    2.选择一个不为0的红色的数,选择一个相邻格的数,将其变为红色,操作完毕后该数的数字-1
  • 思路:贪心,可以发现一段非0的数段中,如果有2,花费一枚硬币将其变红,那么这个非0数段包括左右端的0都能直接变红,如果没2(全是1),那么看左端的0是否已经变红了,如果变红了就去变右端的0(从左到右枚举)。总结下来就是先扫描一遍含2非0段,都操作完后扫描全1非0段,最后依次扫描还没有变红的,用硬币即可。
void BlueFire()
{
    int n;
    cin >> n;
    vector<int> a(n + 1);
    vector<bool> st(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    int ans = 0;
    // 贪心先改2
    for (int i = 1; i <= n; i++)
    {
        if (a[i] == 2 && !st[i])
        {
            ans++;
            st[i] = 1;
            int j = i - 1;
            // 左
            while (j > 0 && a[j])
                st[j--] = 1;
            if (j > 0)
                st[j] = 1;
            // 右
            j = i + 1;
            while (j <= n && a[j])
                st[j++] = 1;
            if (j <= n)
                st[j] = 1;
        }
    }

    // 再改1
    for (int i = 1; i <= n; i++)
    {
        if (a[i] == 1 && !st[i])
        {
            ans++;
            st[i] = 1;
            int j = i - 1;
            bool flag = 0;
            while (j > 0 && a[j])
                st[j--] = 1;
            if (j > 0 && !st[j])
            {
                flag = 1;
                st[j] = 1;
            }
            j = i + 1;
            while (j <= n && a[j])
                st[j++] = 1;
            if (j <= n && !st[j] && !flag)
                st[j] = 1;
        }
    }

    for (int i = 1; i <= n; i++)
        if (!st[i])
            ans++;
    cout << ans << endl;

    return;
}
posted @ 2023-07-28 16:32  布鲁诺邦古耐夫  阅读(288)  评论(0)    收藏  举报