其他分享
首页 > 其他分享> > 【题解】Codeforces Round #700 (Div. 2)

【题解】Codeforces Round #700 (Div. 2)

作者:互联网

前言

蒟蒻的第一把\(Codeforces\),不出意料地成功考砸了。最大的感触就是这次\(CF\)的题目基本都是思维题,很少会出现本蒟蒻刷的水题和模板题。本次比赛的\(A\)题明显是签到题,\(B\)题和\(C\)题涉及的知识点都比较基础,考验的是选手的思维水平。\(D1\),\(D2\)和\(E\),蒟蒻目前的水平还不足以总结。

总体来说,这把打得还算中规中矩,是蒟蒻的真实实力。不像前段时间打的几场水赛,随便打打暴力就混过去了,对实力的提升一点帮助都没有。值得一提的是,\(2\)小时\(15\)分钟的比赛时长真的很可以,本蒟蒻甚至没有扫过一眼后三题的题面。看来,男人不仅要有爆发力和持久,还要追求速度。

\(T1\) \(Yet\) \(Another\) \(String\) \(Game\)

题目链接

题目大意

目前有两个人正在玩游戏。每次可以从给定的由小写字母字符串\(s\)中挑选出一个没有被修改过的位置,将其修改成另外一个和原来不同小写字母。玩家\(A\)的目的是让最终的字符串\(s\)字典序尽量小,玩家\(B\)的目的是让最终的字符串\(s\)字典序尽量大。已知\(A\)是先手,\(B\)是后手,试求字符串\(s\)最终的样子。

输入数据由\(t\)组数据组成,\(t \leq 1000\),字符串的长度\(|s| \leq 50\)。

解题思路

显然,如果要对字典序产生最大的影响,我们一定会选择位置最靠前的位置进行修改。为了让字典序尽量小,\(A\)一定会把这个字母修改成\(a\),如果原来是\(a\)则修改成\(b\);为了让字典序尽量大,\(B\)一定会把这个字母修改成\(z\),如果原来是\(z\)则修改成\(y\)。

\(A\)和\(B\)轮流修改,并且两人一定会选择最靠前的位置。因此,假设第一个位置为\(0\),则偶数位置会被\(A\)修改,奇数位置会被\(B\)修改。按照最优的策略模拟即可。

参考代码

#include <iostream>
#include <string>
using namespace std;

int t;
string s;

int main()
{
    cin >> t;
    while (t--)
    {
        cin >> s;
        for (int i = 0; i < s.length(); i++)
        {
            if (!(i % 2))
            {
                if (s[i] != 'a')
                    s[i] = 'a';
                else
                    s[i] = 'b';
            }
            else
            {
                if (s[i] != 'z')
                    s[i] = 'z';
                else
                    s[i] = 'y';
            }
        }
        cout << s << endl;
    }
    return 0;
}

\(T2\) \(The\) \(Great\) \(Hero\)

题目链接

题目大意

已知有一位英雄和\(n\)只怪物。英雄和怪物都有其对应的生命值和攻击力。英雄的初始生命值为\(B\),攻击力为\(A\)。第\(i\)头怪物的生命值为\(b_{i}\),攻击力为\(a_{i}\)。每一次,英雄都可以选择一只未死亡的怪物并与其战斗,战斗过后英雄和该怪物的生命值分别会变成\(B - a_{i}\)和\(b_{i} - A\)。如果生命值 \(\leq 0\),代表英雄或怪物已经死亡,否则代表存活。试求英雄能否杀死所有的怪物,注意,英雄和最后一头怪物同归于尽也算是杀死所有的怪物。如果能,输出YES,否则输出NO

输入由\(t\)组数据组成,\(t \leq 10^5\),\(A, B \leq 10^6\),\(n \leq 10^5\),\(a_{i}, b_{i} \leq 10^6\)。

解题思路

显然,杀死一头怪物所要消耗的生命值是固定的。设\(cost_{i}\)为英雄杀死第\(i\)头怪物所需要的生命值,显然,\(cost_{i}\)等于杀死第\(i\)头怪物所需要的攻击次数乘上第\(i\)头怪物的攻击力,即\(cost_{i} = \lceil \frac{b_{i}}{A} \rceil \times a_{i}\)。注意,假如英雄攻击怪物后怪物已经死亡,英雄的生命值依然会减少。

设\(sum\)为\(\sum\limits_{i = 1}^n cost_{i}\)。\(c_{i}\)为杀死其他\(n - 1\)头怪物后,杀死最后的第\(i\)头怪物的前一回合英雄所剩的生命值。\(c_{i}\)等于杀死所有怪物所需要的生命值\(sum\),减去怪物发动的最后一次攻击给英雄造成的伤害\(a_{i}\)。如果\(c_{i} > 0\),说明英雄有能力给怪物最后一击,答案为YES,否则,英雄在给怪物最后一击之前就已经去世,答案为NO

枚举最后一头杀死怪物,如果\(max(c_{i}) > 0\),说明英雄可以杀死所有的怪物,反之,英雄在杀死所有怪物前就已经去世。至此,这道题可以在\(O(nt)\)的时间复杂度内解决,无需贪心策略和排序操作。

参考代码

#include <cstdio>
using namespace std;
#define maxn 100005

int t, n;
long long A, B;
long long a[maxn], b[maxn], c[maxn], h[maxn];

long long get_ceil(long long a, long long b)
{
    if (a % b)
        return a / b + 1;
    return a / b;
}

int main()
{
    bool flag;
    long long sum;
    scanf("%d", &t);
    while (t--)
    {
        sum = 0;
        flag = false;
        scanf("%lld%lld%d", &A, &B, &n);
        for (int i = 1; i <= n; i++)
            scanf("%lld", &a[i]);
        for (int i = 1; i <= n; i++)
        {
            scanf("%lld", &b[i]);
            c[i] = get_ceil(b[i], A);
            sum += c[i] * a[i];
        }
        for (int i = 1; i <= n; i++)
        {
            h[i] = B - sum + a[i];
            if (h[i] > 0)
            {
                flag = true;
                break;
            }
        }
        if (flag)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

\(T3\) \(Searching\) \(Local\) \(Minimum\)

题目链接

题目大意

本题是交互式题目

定义一个\(Local\) \(Minimum\)为\(1\)到\(n\)的全排列\(a\)中的某个位置\(i\),且\(i\)满足\(a_{i}\)小于其左右两侧的最小值,即\(a_{i} < min(a_{i - 1}, a_{i + 1})\)。\(a_{n}\)的右侧\(a_{n + 1} = a_{0} = \infty\)。现在,你可以像评测系统提出不超过\(100\)个问题,作为回答,评测系统会给出第\(i\)个位置上的元素\(a_{i}\)。询问结束后,你需要给出任意一个\(Local\) \(Minimum\)。

\(n \leq 10^5\),\(a_{i} \leq n\)。

解题思路

显然,对于这种查询某个元素的题目,我们需要用二分搜索来解决。显然,我们需要分析这个问题是否单调。

假设\(Local\) \(Minimum\)所在的区间为\(\left[ l, r \right]\),其中点为\(mid\)。现在给出重要前提:目前的中点可能不是答案。假如\(a_{mid} < a_{mid + 1}\),说明区间\(\left[ l, mid \right]\)一定是单调递增的,下面给出证明:

因为中点\(mid\)可能不是答案,且\(a_{mid} < a_{mid + 1}\),说明\(a_{mid} > a_{mid - 1}\),否则\(mid\)就是答案。同理,\(a_{mid - 1} > a_{mid - 2}, a_{mid - 2} > a_{mid - 3}\)……由此可知区间\(\left[ l, mid \right]\)一定是单调递增的。

同理,假如\(a_{mid} > a_{mid + 1}\),问题同样单调。由此可以确定,这道题的正解就是二分。假如\(a_{mid} < a_{mid + 1}\),在左区间内继续查找;反之在右区间内继续查找。

参考代码

#include <cstdio>
using namespace std;
#define maxn 100005

int n;
int a[maxn];

int main()
{
    scanf("%d", &n);
    int l = 1, r = n;
    while (l < r)
    {
        int mid = (l + r) / 2;
        if (!a[mid])
        {
            printf("? %d\n", mid);
            fflush(stdout);
            scanf("%d", &a[mid]);
        }
        if (!a[mid + 1])
        {
            printf("? %d\n", mid + 1);
            fflush(stdout);
            scanf("%d", &a[mid + 1]);
        }
        if (a[mid] < a[mid + 1])
            r = mid;
        else
            l = mid + 1;
    }
    printf("! %d\n", l);
    return 0;
}

标签:int,题解,700,mid,long,leq,怪物,Div,英雄
来源: https://www.cnblogs.com/Ling-Lover/p/700-div2.html