Codeforces Round #700 (Div. 2)ABCD1D2

Codeforces Round #700 (Div. 2)ABCD1D2

A - Yet Another String Game

题意

给定一个由小写字母组成的字符串,每次选择一个尚未被选取过的字符进行修改(范围:小写字母a~z)——必须修改成除当前字母以外的小写字母,直至没有字符可以选取。A的目标是使它字典序越小越好,B的目标是使它字典序越大越好,A先开始修改,两个人都选择最佳策略进行游戏。问最终这个串会变成什么样?

思路

选过就不能再选,所以一定是选剩下的里面最靠前的修改,无论是改大还是改小。遇到a、z,就改成b、y。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e3 + 19;
const ll mod = 1e9 + 7;

char str[N];

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        cin >> str;
        int n = strlen(str);
        for(int i = 0; i < n; i++)
        {
            if(i % 2)
            {
                str[i] = str[i] == 'z' ? 'y' : 'z';
            }
            else
            {
                str[i] = str[i] == 'a' ? 'b' : 'a';
            }
            cout << str[i];
        }
        cout << endl;
    }
    return 0;
}

B - The Great Hero

题意

英雄有 A A A 点伤害, B B B 点生命; n n n 只怪兽的伤害值和初始生命值分别为 a 1 , a 2 , … , a n a_1, a_2, … , a_n a1,a2,,an b 1 , b 2 , … , b n b_1, b_2, … , b_n b1,b2,,bn ,英雄和怪兽每次交战,英雄掉 a i a_i ai 点血,怪兽掉 B B B 点血。英雄可以和怪兽多次交战。问英雄能否杀死所有怪兽?

思路

这题最需要注意的点在样例中已经指出了:如果最后一个怪兽和英雄交战时,英雄死了,怪兽也死了,那么英雄虽死犹荣,他还是算杀死了所有的怪兽。我最开始WA的三发都是在这里。假设有一个伤害极高的怪兽,和一群几乎没伤害的怪兽,我们应该先杀死没伤害的怪兽,把伤害越高的怪兽放在越后面,不然英雄在第一轮就被gank了,把伤害高的放在后面,那么英雄虽然仍然会死亡,但是能杀死所有怪兽。

其他点都挺简单的。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 19;
const ll mod = 1e9 + 7;

struct node
{
    ll a, b;
    bool operator < (const node& x)
    {
        return a < x.a;
    }
} c[N];


int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        ll A, B, n;
        bool flag = 0;
        cin >> A >> B >> n;
        for(int i = 0; i < n; i++)
        {
            cin >> c[i].a;
        }
        for(int i = 0; i < n; i++)
        {
            cin >> c[i].b;
        }
        sort(c, c + n);
        for(int i = 0; i < n; i++)
        {
            ll time =  min((B + c[i].a - 1) / c[i].a , (c[i].b + A - 1) / A);
            B -= time * c[i].a;
            if(B <= 0)
            {
                if(i < n - 1)
                    flag = 1;
                else if(c[i].b > time * A)
                    flag = 1;
                break;
            }
        }
        if(flag)
        {
            cout << "NO" << endl;
        }
        else
        {
            cout << "YES" << endl;
        }
    }
    return 0;
}

C - Searching Local Minimum

题意

给定一个从1到n的排列 p p p ,每次可以询问一个位置的数字,要求你在100次询问内,找到一个位置 i i i ,有 a i − 1 > a i , a i < a i + 1 a_{i - 1} \gt a_i,a_i \lt a_{i + 1} ai1>ai,ai<ai+1

思路

参考:(4条消息) 1480 C. Searching Local Minimum(新颖的二分查找)

挺有意思的题目,赛场上是真的想不出

代码

(其实和参考写的挺像的,可以直接去参考看思路和代码)

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 19;
const ll mod = 1e9 + 7;

int a[N];
int n;

void query(int x)
{
    if(x > n || x < 1 || a[x])
        return ;
    cout << "? " << x << endl;
    cout.flush();
    cin >> a[x];
}

int main()
{
    cin >> n;
    int l = 1, r = n;
    a[0] = INF, a[n + 1] = INF;
    while(l < r)
    {
        int mid = (l + r) >> 1;
        query(mid);
        query(mid + 1);
        if(a[mid] > a[mid + 1])
        {
            l = mid + 1;
        }

        else
        {
            r = mid;
        }
    }
    cout << "! " << l << endl;
    return 0;
}

D1 - Painting the Array I

题意

给你一个数组,把他不打乱顺序的分成两半,并合并这分开的两个数组中所有的连续重复元素。问两个数组最终长度和最大是多少?

思路

其实是很简单的一道题,赛场上的思路也很完美,就是细节太多,死得很惨。思路如下:

  1. 把原数组中的数字拆分存储成 { \{ { 1 1 1 2 2 2 3 3 3 1 , … , k 1,…,k 1,,k n } n\} n} 的形式,即合并连续相同的数字,之后以这样的形式从头到尾遍历;

  2. 我们很容易看出,其实情况完全可以归为两种:

    1. 数字个数为 1 1 1
    2. 数字个数大于 1 1 1
       

    为什么直接把其他情况都合并到2呢?因为不管多少个最后肯定都是分成两堆。所以多少个到最后最多都是合并成两个。

    第一种情况,肯定能给答案增加1的贡献1;第二种情况则需要特判当前分出来的两个队列末尾的字符,会有两种情况,第一种是有一个队列的末尾和当前数字相同,第二个情况是两个队列的末尾都不和当前数字相同2。第一种情况只能提供1的贡献,第二种情况提供2的贡献。

    实现方法很多,就不详细讲实现了(代码应该还是容易看懂的qwq)。

代码

//这题如果很难理解可以画图看看,就能明白了
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 19;
const ll mod = 1e9 + 7;

queue<P> que;

int main()
{
    int n;
    cin >> n;
    int cur;
    int cnt = 1;
    cin >> cur;
    for(int i = 1; i < n; i++)
    {
        int x;
        cin >> x;
        if(x != cur)
        {
            que.push(P(cur, cnt));//转化成数字cur有cnt个的形式
            cur = x;
            cnt = 1;
        }
        else
        {
            cnt++;
        }
    }
    que.push(P(cur, cnt));
    ll ans = 0;
    int flag = 0;
    int tmp;
    while(!que.empty())
    {
        P p = que.front();
        if(flag)//flag > 0标志着如果我要把现在手上这堆数字分成两半,有可能出现贡献只有1的情况
        {
            if(p.second > 1)//当前这堆个数超过1
            {
                if(p.first == tmp)
                    ans++;
                else
                    ans += 2;
                flag = 2;
                tmp = p.first;
            }
            else//为什么当前这堆个数不超过1也要判断?因为也会影响到flag的值,这就是我赛场上错的地方
            {
                if(p.first != tmp)
                {
                    flag--;
                }
                else
                {
                    flag = 2;
                }
                ans++;
            }
        }
        else
        {
            if(p.second > 1)
            {
                ans += 2;
                flag = 2;
                tmp = p.first;
            }
            else
            {
                ans += 1;
            }
        }
        que.pop();
    }
    cout << ans << endl;
    return 0;
}

D2 - Painting the Array II

题意

和D1的区别仅在于,D1求最大长度,D2求最短长度。

思路

参考:Submission #107024651 - Codeforces

看了很多人的博客,做法从贪心到堆优化dp、线段树dp都有,贪心的实现很多使用的是记录下一个该数字出现的位置 next[i],dp也比较复杂。最后在看codeforces的status的时候看到了这份代码,感觉实现的很简单、清晰,所以就参考他的代码写出了我的。

主要思路:(下面将0 1区分开后的两个数组称为两个队伍)

  1. 把所有的多个数字都合并成一个(这点应该不用我多说),使得变化后的数组中没有连续相同的数字;

  2. 从头到尾遍历,用set来记录可能是两个队伍末尾的数字。这样说可能很难理解,那么我们先分类讨论set如何变化:

    1. 如果当前数字不能在set中,则加入set;
    2. 如果当前数字在set中,清空set,将当前位置与当前位置的前一个位置的两个数组加入set

     
    显然,set维护的其实是一连串不同的数字,通过不同的划分方案,这些数字都有可能成为两条队伍的末尾

    遍历过程中一旦出现一个数字,它在set中已经有了对应元素,那么这两个相同的数字一定能通过一个划分方案位置相邻,完成我们缩短队伍长度的目的。

    在相邻之后,队伍末尾就确定下来了,一定是当前数字和上一个数字(可以理解为,把两个相同数字中间夹着的数字全部移动到另一个队伍里去了,那么最末尾的数字肯定是这个数的上一个数了)。

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 19;
const ll mod = 1e9 + 7;

int a[N];
vector<int> vec;

int main()
{
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        if(a[i] != a[i - 1])
            vec.push_back(a[i]);
    }
    if(vec.back() != a[n])
        vec.push_back(a[n]);
    set<int> st;
    ll ans = 0;
    for(int i = 0; i < vec.size(); i++)
    {
        if(st.find(vec[i]) != st.end())
        {
            st.clear();
            st.insert(vec[i]);
            st.insert(vec[i - 1]);
        }
        else
        {
            st.insert(vec[i]);
            ans++;
        }
    }
    cout << ans << endl;
    return 0;
}

总结

还是心态问题+身体问题,导致继续掉分。B卡了之后没有重新思考是不是从方法上有问题,而是一意孤行认为是数据范围的问题;D1思路完全正确,却因为细节败的很惨。做题的时候就安心做题,是否放弃比赛应该在赛前想好,出错了应该冷静思考,而不是一边想着放弃一边因为掉分急得无法思考。以后的现场赛更考验心态呀!要加油了

多多包涵,共同进步


  1. 因为经过第一步的转换后,这个单独的数字一定和它前后都是不同的 ↩︎

  2. 为什么不会有都相同的情况?和这段开头的注释1一样 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值