Educational Codeforces Round 174 (Rated for Div. 2) (A~D)

前言

将近一年没有更新博客了,突发奇想更新一下。(主要原因是好久没打CF了...)
长时间没写题,场上脑子转不过来,代码写的又长又臭。

A. Was there an Array? (签到)

题目问是否存在这种构造,顺便拉个样例 \(222333\) 发现两个 \(1\) 之间至少存在两个 \(0\), 检查一下即可

void solve(){
    cin >> n;
    for(int i = 1; i <= n - 2; i++) {
        cin >> a[i];
    }
    int flag = 0; // 表示前面是否有1
    int cnt = 0; // 表示离上个1之间有多少0
    for(int i = 1; i <= n - 2; i ++) {
        if(flag == 0 && a[i] == 1) {
            flag = 1;
            cnt = 0;
        }
        if(flag) {
            if(a[i] == 0) {
                cnt ++;
            }
            else {
                if(cnt == 1) { // 可能存在 11 的情况,这里不能 cnt<=1
                    cout << "NO\n";
                    return;
                }
                cnt = 0;
            }
        }
    }
    cout << "YES\n";
}

B. Set of Strangers (签到)

题意是每次操作选取一些不相邻的相同颜色格子,涂上任意一种颜色,求最终所有格子颜色相同需要几次操作。
一个显然的结论是一个颜色最多用两次操作就能全部选取到。
判断是不是有相同颜色的格子相邻,是的话就要操作两次,否则一次即可。
最后贪心一下。

void solve(){
    cin >> n >> m;
    cnt[0] = cnt[1] = cnt[2] = 0; // 表示需要一次操作与两次操作的颜色数量
    for(int i = 1; i <= n * m ;i++) vis[i] = 0;  // vis表示该同种颜色是否相邻,1表示出现过但不相邻,2表示有相邻情况
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            cin >> a[i][j];
            if(vis[a[i][j]] == 0) {
                vis[a[i][j]] = 1;
            }
            else if(a[i - 1][j] == a[i][j] || a[i][j - 1] == a[i][j]) {  //判断相邻
                vis[a[i][j]] = 2;
            }
        }
    }
    for(int i = 1; i <= n * m; i++) {
        cnt[vis[i]]++;
    }
    if(cnt[2] == 0) {    // 贪心取答案
        cout << cnt[1] - 1 << endl;
    }
    else {
        cout << (cnt[2] - 1) * 2 + cnt[1] << endl;
    } 
}

C. Beautiful Sequence (简单dp)

题意说的很复杂,又是条件,又是举例,实际上都没什么用...
由于元素只有 \({1,2,3}\) ,所以只有形如 \([1,2,3],[1,2,2,3]\)\(1,3\)为两端,中间都是\(2\)的可行。求这种子序列的数量。
暴力思想就是枚举\(1,3\),计算其中有多少个 \(2\) 。根据组合数学中间有 \(x\)\(2\) 的贡献是 \(2^x - 1\)
想法是计算到目前为止,对于所有可能左端点\(1\)的方案数之和进行转移。
每个\(1\)都能产生\(2^x-1\)的贡献,其中 \(x\) 我们不方便保存,但\(-1\)容易,因此保存用\(cnt\)数组记录\(1\)的个数。
虽然 \(x\) 不能保存,但是会发现每新增一个\(2\),都会使前面每一段的\(x+1\)。也就是贡献翻倍。
转移随便分类一下,见代码。

void solve(){
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    int ans = 0;
    for(int i = 1; i <= n; i++) {
        if(a[i] == 1) {
            pre[i] = pre[i - 1] + 1;
            cnt[i] = cnt[i - 1] + 1;
            pre[i] %= mod;
        }
        else if(a[i] == 2) {
            pre[i] = pre[i - 1] * 2;
            cnt[i] = cnt[i - 1];
            pre[i] %= mod;
        }
        else {
            pre[i] = pre[i - 1];
            cnt[i] = cnt[i - 1];
            int x = (pre[i] - cnt[i] + mod) % mod;
            ans = (ans + x) % mod;;
        }
    }
    cout << ans << endl;
}

D. Palindrome Shuffle (贪心)

纯贪心删删改改莫名其妙就过了。
赛后看了眼标签,二分,哈希一个都没用上。
题意是重新排列一个连续子串使字符串回文,求最短子串长度。
1.两端已经对称的就不用改了,把两端忽略掉。
2.剩下的如果分成左右两半,如果字母数量一样,则可以只修改一侧使其对称
3.如果字母数量不一样,表示子串需要覆盖两侧,枚举从左到右和从右到左哪个更优。
题目给的样例有大部分情况,模拟一下即可。

#include<bits/stdc++.h>
using namespace std;
#define SHOJIG ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl '\n' 
#define x1 xxxx
#define y1 yyyy
#define int long long
#define lowbit(x) (x & (-x))
typedef long long ll;
const int N = 1e6 + 9, inf = 0x3f3f3f3f;
int n, m, k;
string s;
int cnt[30], tmp[30],lef[30], rig[30];  // 史山代码
int check1() { // 如果一侧的字母都能在另一部分找到,就是可能的答案
    for(int i = 0; i <= 25; i++) {
        if(lef[i] < tmp[i] - lef[i]) return 0;
    }
    return 1;
}
int check2() {
    for(int i = 0; i <= 25; i++) {
        if(rig[i] < tmp[i] - rig[i]) return 0;
    }
    return 1;
}
void solve(){
    cin >> s;
    for(int i = 0; i <= 25; i++) cnt[i] = tmp[i] = lef[i] = rig[i] = 0;
    int n = s.length();
    s = " " + s;
    int l = 1, r = n;
    while(l < r && s[l] == s[r]) {
        l++;
        r--;
    } 
    if(l >= r) {
        cout << 0 << endl;
        return;
    }
    // case 1:
    int l1 = n / 2, r1 = n / 2 + 1, flag = 1;
    for(int i = l ; i <= l1; i++) { // 左右两半字母数量是否相同
        cnt[s[i] - 'a']++;
    }
    for(int i = r1; i <= r; i++) {
        cnt[s[i] - 'a']--;
    }
    for(int i = 0; i <= 25; i++) {
        if(cnt[i] != 0) {
            flag = 0;
        }
    }
    if(flag == 1) { // 相同的情况从中心再开始判断回文,尽可能减少需要改动的长度
        while(s[l1] == s[r1]) {
            l1--;
            r1++;
        }
        cout << l1 - l + 1 << endl; // 任取一边修改
        return;
    }
    else {
        for(int i = l; i <= r; i++) { // 记录中间总共字母数量
            tmp[s[i] - 'a']++;
        }
        // left 从左到右覆盖
        int ans = r - l + 1, res = 0; // res记录长度
        for(int i = l; i <= r; i++) {
            res++;
            lef[s[i] - 'a']++;
            if(check1()) {
                ans = min(res, ans);
            }
        }
        // right 从右到左覆盖
        res = 0;
        for(int i = r; i >= l; i--) {
            res++;
            rig[s[i] - 'a']++;
            if(check2()) {
                ans = min(res, ans);
            }
        }
        cout << ans << endl;
    }
}
signed main(void){
    SHOJIG 
    //init();
    int _ = 1;
    cin >> _;
    for(int i = 1;i <= _; i++) solve();
}
posted @ 2025-02-19 01:05  SHOJIG  阅读(323)  评论(1)    收藏  举报