Codeforces Round #616 (Div. 2)解題報告


Codeforces Round #616 (Div. 2)解題報告

A. Even But Not Even

找兩個奇數就行了。

#include<bits/stdc++.h>
using namespace std;
void solve()
{
    int n; string s;
    cin >> n >> s;
    string ans = "";
    for(int i = 0; i < n; i++)
    {
        if(int(s[i] - '0')%2 == 1)
            ans += s[i];
        if(ans.size() == 2) break;
    }
    if(ans.size() == 2) cout << ans << endl;
    else puts("-1");
}
int main()
{
    int T; scanf("%d", &T);
    while(T--) solve();
    return 0;
}

B. Array Sharpening

貪心+構造。

就從0開始往上走,看看最遠能走到哪,看代碼。

#include<bits/stdc++.h>
#define PII pair<int, int>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 10;
int a[maxn], b[maxn];

inline void solve()
{
    int n; scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    if(n == 1)
    {
        puts("Yes");
        return;
    }
    b[n] = 0; int pos = 0;
    for(int i = n-1; i >= 1; i--)
    {
        b[i] = b[i+1]+1;
        if(a[i] >= b[i]) continue;
        else {
            pos = i+1;
            break;
        }
    }

    for(int i = 1; i <= pos; i++)
    {
        b[i] = i-1;
        if(b[i] <= a[i]) continue;
        else {
            puts("No");
            return;
        }
    }
    puts("Yes");
}

int main()
{
    int T; scanf("%d", &T);
    while(T--) solve();
    return 0;
}

C. Mind Control

這道題題意很坑,其實是要選擇最前面的\(k\)個人。

假設原先的數字序列是一個\(deque\),那假如說前面拿\(i\)個,后面就會拿\(k-i\)個。

之后留下一個新的\(deque\)

我排在第\(m\)位,我控制了\(k\)個人,所以我前面還有\(m-k-1\)個人拿才輪到我。

假設說前面有\(j\)個人拿,那后面就會拿\(m-k-1-j\)個人。

那這時候就要搞清楚,最開始的\(i\)\(k-i\)是由我做決定的。

\(j,m-k-1-j\)是由別人做決定的。

最后會露出隊頭隊尾,我會取一個較優的,這是我來做決定。

所以我們枚舉\(i,j\),然后找最大。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e3 + 10;
int n, m, k;
int a[maxn];

inline void solve()
{
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    k = min(m-1, k);
    int ans = 0;

    for(int i = 0; i <= k; i++)
    {
        int tmp = 1e9;
        for(int j = 0; j <= m-k-1; j++)
        tmp = min(tmp, max(a[i+j+1], a[n-(k-i)-(m-k-1-j)]));
        ans = max(ans, tmp);
    }
    cout << ans << endl;
}

int main()
{
    int T; scanf("%d", &T);
    while(T--) solve();
    return 0;
}

D. Irreducible Anagrams

閱讀理解題,先看看題意。

規定兩個字符串的\(anagrams\):兩個字符串中間字符出現的次數一樣。

規定\(reducible\),有一個\(k\geq2\),然后有\(s_1,t_1,s_2,t_2,...,s_k,t_k\)這幾個子串都是對應的\(anagrams\)

規定\(irreducible\),就是不\(reducible\)

給定一個字符串,和\(q\)次詢問,每次詢問給定一個\(l,r\),問這個子串能否構造出一個\(irreducible\)

思路:分類討論

  • 1:如果長度為\(1\)

    • 因為必須要\(k\geq 2\),所以一定能有irr...
  • 2:子串首尾不相同。

    • 那這時候只需要構造一個字符串,構造的字符串的首尾和原字符串首尾是反的。
    • 這樣的話就可以保證從前面開始的子串到一個位置\(k\),有\(s(1...k),t(1...k)\)一定不是\(anagrams\)
  • 3:字符串中有三個或三個以上的不同的字符。

    • 因為如果有三個不同的字符,那么一定能構造出左右邊界處連續兩個不同。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int q, cnt[26][maxn], l, r;
string s;

void solve()
{
    scanf("%d%d", &l, &r);
    l--, r--;
    //長度為1
    if(l == r) {
        puts("YES");
        return;
    }
    //左右不同
    if(s[l] != s[r]){
        puts("YES");
        return;
    }
    //字符出現次數大於等於3
    int tot = 0;
    for(int i = 0; i < 26; i++)
    {
        int R = cnt[i][r], L = 0;
        if(l > 0) L = cnt[i][l-1];
        if(R - L > 0) tot++;
    }

    if(tot >= 3) {
        puts("YES");
        return;
    }
    puts("NO");
}

int main()
{
    cin >> s; scanf("%d", &q);
    for(int i = 0; i < s.size(); i++)
    {
        cnt[s[i]-'a'][i]++;
        if(i > 0){
            for(int j = 0; j < 26; j++)
                cnt[j][i] += cnt[j][i-1];
        }
    }
    while(q--) solve();
    return 0;
}

E. Prefix Enlightenment

題意:

你有n個燈,編號從1到n,每個燈的初始狀態時關(0)或者開(1)。

你有\(k\)個集合\(A_1,...,A_k\),其中任意三個集合的交集為空集,集合控制着一些燈的開關。

每次操作你可以選擇一個集合,然后改變這個集合控制的燈的狀態。

詢問你讓前\(i\)個燈泡全部同時開着需要最少多少次操作,其中\(i+1\)~\(n\)的燈的狀態可以不用理會。

思路:

qsc的視頻題解https://www.bilibili.com/video/av86529667?p=5

由於任意三個集合的交集為空集,這就說明對於一個燈泡,他的開關最多只在兩個集合當中。

也就是說:

  • 一個燈泡由兩個開關集合控制。
  • 一個燈泡由一個開關集合控制。

對於每個開關集合,他有兩個狀態,一個是使用這個開關集合,一個是不使用這個開關集合。

我們先看第一個燈泡,同時假設這個燈泡由第\(i,j\)個集合管理,那么會有兩個狀態:

  • 燈泡是關的。

    • 1:第\(i\)個開關是開的,第\(j\)個開關是閉的。
    • 2:\(i\)閉,\(j\)開。
  • 燈泡是開的。

    • 1:\(i\)開,\(j\)開。
    • 2:\(i\)閉,\(j\)閉。

所以我們就可以根據上述關系用並查集縮點。

同時特殊處理由一個開關集合控制的燈泡情況,具體見代碼,詳細注釋。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 6e5+10;
int n, k, l[maxn][2];
int fa[maxn], sz[maxn];
string s;

int get_fa(int x){
    if(x == fa[x]) return x;
    return fa[x] = get_fa(fa[x]);
}

//計算x開和關兩個選擇操作的最小值
int calc(int x)
{
    //y表示對應的開關集合(開/關)
    int y = x<=k?x+k:x-k;
    x = get_fa(x), y = get_fa(y);
    //如果說有一個為0 那就只能進行選另一個操作了
    //(只有在某個燈泡只被一個開關控制才會置0)
    if(x == 0 || y == 0) return sz[x+y];
    //返回較小值
    return min(sz[x], sz[y]);
}

void merge_dis(int x, int y)
{
    x = get_fa(x); y = get_fa(y);
    if(y == 0){
        swap(x, y);
    } fa[y] = x;
    if(x != 0) sz[x] += sz[y];
}

int main()
{
    scanf("%d%d", &n, &k); cin >> s;

    //種類帶權並查集
    //i表示第i個開關集合不使用的狀態
    //i+k表示第i個開關集合使用的狀態
    //sz表示花費是多少
    //初始狀態使用一次 所以置1
    for(int i = 1; i <= k; i++)
    fa[i] = i, fa[i+k] = i+k, sz[i+k] = 1;

    //l(x, 0/1)=i 表示管理燈泡x的開關集合i
    //最多有兩個
    for(int i = 1, c; i <= k; i++)
    {
        scanf("%d", &c);
        for(int j = 0, x; j < c; j++)
        {
            scanf("%d", &x);
            if(l[x][0] == 0) l[x][0] = i;
            else l[x][1] = i;
        }
    }

    int ans = 0;
    for(int i = 1; i <= n; i++)
    {
        //第i個燈泡只由一個開關控制
        if(l[i][1] == 0)
        {
            int x = l[i][0];
            //如果x等於0 也就是沒有關聯
            //任何的開關集合 那么這時候
            //就不用考慮 continue
            if(x)
            {
                //先減去上一次的答案
                ans -= calc(x);
                //如果說一開始就亮了 那么這個開關集合就不需要點
                //同時也是一種標記 表示這種狀態不可變
                if(s[i-1] == '1') fa[get_fa(x+k)] = 0;
                else fa[get_fa(x)] = 0;
                ans += calc(x);
            }
        }
        else //兩個開關
        {
            //x y對應的開關集合
            int x = l[i][0], y = l[i][1];
            //如果一開始燈就是開的
            if(s[i-1] == '1')
            {
                //這兩個開關還沒有關系
                //如果有關系 那這個燈就開了
                if(get_fa(x) != get_fa(y))
                {
                    //先減去上一次的答案
                    ans -= calc(x);
                    ans -= calc(y);
                    //縮點
                    //兩個開關同時不使用
                    merge_dis(x, y);
                    //兩個開關同時使用
                    merge_dis(x+k, y+k);
                    //然后再加上這次的結果
                    ans += calc(x);
                }
            }
            //如果燈是關的
            else
            {
                if(get_fa(x+k) != get_fa(y))
                {
                    ans -= calc(x);
                    ans -= calc(y);
                    merge_dis(x+k, y);
                    merge_dis(x, y+k);
                    ans += calc(x);
                }
            }
        }
        cout << ans << endl;
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM