Codeforces Round #789 (Div. 2) A-C
阅读原文时间:2023年07月09日阅读:2

Codeforces Round #789 (Div. 2) A-C

题目

https://codeforces.com/problemset/problem/1677/A

题解

思路

知识点:模拟。

(比较显然,不写了)

时间复杂度 \(O(nlogn)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

int a[100];

int main(){
    std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i = 0;i<n;i++){
            cin>>a[i];
        }
        sort(a,a+n);
        int cnt = 0;
        bool ok = false;
        for(int i = 0;i<n-1;i++){
            if(a[i] == a[i+1] || !a[i]){
                ok = true;
                break;
            }
        }
        for(int i = 0;i<n;i++){
            if(a[i]) cnt++;
        }
        cout<<(cnt+!ok)<<'\n';
    }
    return 0;
}

题目

https://codeforces.com/problemset/problem/1678/B1

https://codeforces.com/problemset/problem/1678/B2

题解

思路

知识点:贪心。

对于给定偶数长度的0/1串,奇数子串与其他串(不论奇偶的其他)的分界点有且仅有一个位于某一对中间。比如,\(111001100011\) 划分以后变成 \(11,10,01,10,00,11\) ,发现 \(111\) 的末尾 \(1\) 出现在 \(10\) 中,\(000\) 的首部 \(0\) 出现在 \(10\) 中。因此我们可以通过按对(即两个两个不相交)遍历,若遇到一次 \(01\) 或 \(10\) 则操作次数加 \(1\) 。

与此同时,我们发现若修改一处 \(01\) 或 \(10\) 可以修改成 \(00\) 或 \(11\) ,从而被前段或者后段的 \(00\) 或 \(11\) 吸收不改变总段数,因此可以将修改等价认为直接删除这段 \(01\) 或 \(10\) ,即在程序中不考虑这种情况对总数影响,只需要记录 \(00\) 或 \(11\) 的连续成段情况、特别地,如果 \(0/1\) 串本身没有 \(00\) 或 \(11\) 的对,或者说全串由 \(01\) 或 \(10\) 组成,那么它们可以自成唯一一段互相吸收,需要特判。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

int main(){
    std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        string s;
        cin>>s;
        int op = 0,cnt = 0;
        char pre = '?';
        for(int i = 0;i<n;i+=2){///两两配对
            if(s[i] != s[i+1]) op++;///操作次数+1
            else{
                if(pre != s[i]) cnt++;///不同数字,段数+1
                pre = s[i];///更新段数字
            }
        }
        cout<<op<<' '<<max(1,cnt)<<'\n';///如果没有00/11吸收01/10,那么01/10可以自成一段
    }
    return 0;
}

题目

https://codeforces.com/problemset/problem/1677/A

题解

思路

知识点:DP,枚举。

注意到 \(a<b<c<d\) ,可以考虑枚举 \(b,c\) 两个点,用 \(a,d\) 分别在 \([1,b-1]\) 和 \([c+1,n]\) 的区间匹配。

匹配条件是 \(p_a < p_c \and p_b >p_d\) ,考虑用数组 \(cnt[i][j]\) 表示满足 \(p_x \leq j , x \in [1,i]\) 的 \(x\) 个数。于是 \(a\) 的匹配个数是 \(cnt[b-1][p[c]]\) ,\(d\) 的匹配个数是 \(cnt[n][p[b]] - cnt[c][p[b]]\) 。因此,对于一组 \(b,c\) 可以得到 \(cnt[b-1][p[c]] \cdot (cnt[n][p[b]] - cnt[c][p[b]])\) 的组数,枚举 \(b,c\) 累加即可。

时间复杂度 \(O(n^2)\)

空间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>

using namespace std;

int p[5007],cnt[5007][5007];//注意初始化

int main(){
    std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i = 1;i<=n;i++){
            cin>>p[i];
        }
        for(int i = 1;i<=n;i++){///预处理cnt[i][j] 为 [1,i] 中小于等于 j 的数字个数
            for(int j = 1;j<=n;j++) cnt[i][j] = cnt[i-1][j];///传递上个区间
            for(int j = p[i];j<=n;j++) cnt[i][j]++;///j从p[i]开始都大于等于i,遍历+1
        }
        ///枚举b,c,以此为准求[1,b-1]和[c+1,d]间合法a,d个数
        long long ans = 0;
        for(int b = 2;b<=n-2;b++){
            for(int c = b+1;c<=n-1;c++){
                ans += 1LL * cnt[b-1][p[c]] * (cnt[n][p[b]] - cnt[c][p[b]]);
            }
        }
        cout<<ans<<'\n';
    }
    return 0;
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器