天天看點

Codeforces Round #700 (Div. 2)ABCD1D2Codeforces 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} ai−1​>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一樣 ↩︎

繼續閱讀