Codeforces Round #616 (Div. 2)解题报告

Posted zxytxdy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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),有一个(kgeq2),然后有(s_1,t_1,s_2,t_2,...,s_k,t_k)这几个子串都是对应的(anagrams)

规定(irreducible),就是不(reducible)

给定一个字符串,和(q)次询问,每次询问给定一个(l,r),问这个子串能否构造出一个(irreducible)

思路:分类讨论

  • 1:如果长度为(1)
    • 因为必须要(kgeq 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;
}

以上是关于Codeforces Round #616 (Div. 2)解题报告的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #616 题解

Codeforces Round #616 (Div. 2)

Codeforces Round #616 (Div. 2)

Codeforces Round #616 (Div. 1)

Codeforces Round #616 部分题解

Codeforces Round #616(Div.2) Even But Not Even