Codeforces Round #789 (Div. 2)

CF系列题解

Codeforces Round #789 (Div. 2)

  • 题目
    • A. Tokitsukaze and All Zero Sequence
      • 原题链接
      • 题意
    • 题解
      • 思路
      • 代码
    • B1. Tokitsukaze and Good 01-String (easy version)
      • 原题链接
      • 题意
    • 题解
      • 思路
      • 代码
    • B2. Tokitsukaze and Good 01-String (hard version)
      • 原题链接
      • 题意
    • 题解
      • 思路
      • 代码
    • C. Tokitsukaze and Strange Inequality
      • 原题链接
      • 题意
    • 题解
      • 思路
      • 代码


题目

A. Tokitsukaze and All Zero Sequence

原题链接

A. Tokitsukaze and All Zero Sequence

题意

给定一个序列,可以进行如下操作:

  1. 将两个不同的数 a i , b i a_i,b_i ai,bi 变为 他们当中的最小值。
  2. 对于两个相同的数,我们可以将其中一个变为 0 0 0

问将所有数变为 0 0 0 所需要的最少操作次数。

题解

思路

分类讨论:

  1. 当序列中存在 0 0 0,我们可以依次将所有的非零数通过操作1转化为 0 0 0,答案即为非零数的数量。
  2. 若不存在 0 0 0,我们看其中是否有相同的数,若存在相同的数,我们可以将其中一个变为 0 0 0,再把其他数进行思路1,答案即为 n n n
  3. 若不存在相同数,我们可以先把两个数变相同,之后进行思路2,答案即为 n + 1 n+1 n+1

实现上用了排序。

代码

#include 

using namespace std;

using i64 = long long;

void solve()
{
    int n;
    cin >> n;
    
    vector<int> a(n);
    for (int i = 0; i < n; i ++ ) cin >> a[i];

    sort(a.begin(), a.end());

    if (a[0] == 0) {
        int i = 0;
        while (i < n && a[i] == 0) i ++ ;
        cout << n - i << "\n";
    } else {
        bool flag = false;
        for (int i = 0; i < n - 1; i ++ ) {
            if (a[i] == a[i + 1]) {
                flag = true;
                break;
            }
        }
        if (flag) {
            cout << n << "\n";
        } else {
            cout << n + 1 << "\n";
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}

B1. Tokitsukaze and Good 01-String (easy version)

原题链接

B1. Tokitsukaze and Good 01-String (easy version)

题意

给定一个 01 字符串,倘若每个连续的相同(即全为 0 或全为 1 )的段其长度为偶数,我们则认为其是好的。问将一个字符串变好需要的最少操作次数。

题解

思路

由于要求每段长度是偶数,那么(下标从 0 0 0 开始)应该满足 s 0 = s 1 , s 2 = s 3 , . . . s_0=s_1,s_2=s_3,... s0=s1,s2=s3,...,对于不满足的情况我们需要改变其中一个,因此 ans++

代码

#include 

using namespace std;

using i64 = long long;

void solve()
{
    int n;
    cin >> n;

    string s;
    cin >> s;

    int ans = 0;
    for (int i = 0; i + 1 < n; i += 2) {
        if (s[i] != s[i + 1]) {
            ans ++ ;
        }
    }

    cout << ans << "\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}

B2. Tokitsukaze and Good 01-String (hard version)

原题链接

B2. Tokitsukaze and Good 01-String (hard version)

题意

上一题的扩展,在满足最小操作次数的同时要使得段数尽可能少,问操作次数和最小的段数。

题解

思路

比赛又写了一大坨代码,回头理一理思路再写。

代码

#include 

using namespace std;

void solve()
{
    int n;
    cin >> n;
    string s;
    cin >> s;
    
    int ans0 = 0, res0 = 0;
    bool flag = false;
 
    vector<int> id(n / 2);
 
    for (int i = 0; i < s.size(); i ++ ) {
        if(i == 0) {
            if(s[i] == '0') flag = true;
            res0 ++;
        } else {
            if(flag == true) {
                if(s[i] == '1' && (res0 & 1)) {
                    s[i] = '0';
                    ans0 ++;
                    res0 ++;
                    id[i /2] = 2;
                } else if(s[i] == '1' && res0 % 2 == 0) {
                    flag = false;
                    res0 = 1;
                } else {
                    res0 ++;
                }
            } else if(flag == false) {
                if(s[i] == '0' && (res0 & 1)) {
                    s[i] = '1';
                    ans0 ++;
                    res0 ++;
                    id[i / 2] = 2;
                } else if(s[i] == '0' && res0 % 2 == 0) {
                    flag = true;
                    res0 = 1;
                } else {
                    res0 ++;
                }
            }
        }
 
    }
    n = id.size();
    for (int i = 0; i < s.size(); i += 2)  {
        if (id[i / 2] != 2) {
            if (s[i] == '1') id[i / 2] = 1;
            else id[i / 2] = 0;
        }
    }
    int la = -1, res = 1, res1 = 1, res2 = 1;
 
    if(id[0] == 2) {
        id[0] = 1;
        la = 1;
        for(int i = 1; i < n; i ++ ) {
            if(id[i] == 2 || id[i] == la) {
                continue;
            } else {
                la = id[i];
                res1 ++;
            }
        }
        id[0] = 0;
        la = 0;
        for(int i = 1; i < n; i ++ ) {
            if(id[i] == 2 || id[i] == la) {
                continue;
            } else {
                la = id[i];
                res2 ++;
            }
        }
        res = min(res1, res2);
    } else {
        for(int i = 0; i < n; i ++ ) {
            if(la == -1) {
                la = id[i];
                continue;
            } else  {
                if(id[i] == 2 || id[i] == la) {
                    continue;
                } else {
                    la = id[i];
                    res ++;
                }
            }
        }
    }
    cout << ans0 << ' ' << res << "\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}

C. Tokitsukaze and Strange Inequality

原题链接

C. Tokitsukaze and Strange Inequality

题意

给定排列 p p p,求满足 p a < p c , p b > p d p_ap_d pa<pc,pb>pd 的四元组 [ a , b , c , d ] ( 1 ≤ a < b < c < d ≤ n ) [a,b,c,d] (1≤a[a,b,c,d](1a<b<c<dn) 的个数。

题解

思路

由数据范围可知复杂度 O ( n 2 ) O(n^2) O(n2),因此肯定是枚举,我们可以枚举 b , c b, c b,c a a a 即为 b b b 的左边比 c c c 小的数, d d d 即为 c c c 的右边比 b b b 小的数,然后就发现很像树状数组求逆序对啊!!!

考虑一下复杂度, l o g ( 5000 ) log(5000) log(5000) 也就是个常数,而且1.5s时限,加上cf神机,虽然好像时间有点紧,但应该能过。

(带火们好像大多用的是前缀和呜呜呜,太菜了想不到)

代码

#include 

using namespace std;

using i64 = long long;

template <typename T> 
struct Fenwick {
    const int n;
    vector<T> tr;
    Fenwick(int n) : n(n), tr(n) {}
    int lowbit(int x) {
        return x & -x;
    }
    void add(int x, T v) {
        for (int i = x; i < n; i += lowbit(i)) tr[i] += v;
    }
    T query(int x) { 
        T res = 0;
        for (int i = x; i; i -= lowbit(i)) res += tr[i];
        return res;
    }
    T rangeSum(int l, int r) { 
        return query(r) - query(l - 1);
    }
};


void solve()
{
    int n;
    cin >> n;

    vector<int> a(n + 1);
    for (int i = 1; i <= n; i ++ ) cin >> a[i];

    Fenwick<int> fen1(n + 1), fen2(n + 1);
    i64 ans = 0;
    for (int b = 2; b <= n - 2; b ++ ) {
        fen1.add(a[b - 1], 1);
        for (int c = n - 1; c > b; c -- ) {
            fen2.add(a[c + 1], 1);
            ans += fen2.query(a[b] - 1) * fen1.query(a[c] - 1);
        }
        for (int c = n - 1; c > b; c -- ) fen2.add(a[c + 1], -1); // 注意还原
    }

    cout << ans << "\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T -- ) {
        solve();
    }

    return 0;
}

你可能感兴趣的:(codeforces日常,算法,c++)