AtCoder Beginner Contest 346

文章讨论了三种编程问题:1)计算给定序列中排除重复数后的和;2)将字符串变为只有一对相邻数字相同的良好字符串的最小代价;3)统计网格涂色操作后每种颜色的格子数。解题策略涉及数据结构如set和数组,以及优化处理后效性问题。
摘要由CSDN通过智能技术生成

前面两道阅读理解直接跳过。

C - Σ

大意

给定序列A,求[1,k]中所有数的和,但要排除A中出现的数字。

思路

首先1+2+...+k=\dfrac{k(k+1)}{2},算出后减去A中出现过的数即可。

注意要对A去重,或者使用set。

代码

#include<iostream>
#include<vector>
#include<set>
using namespace std;
typedef long long LL;

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    LL n, k;
    cin >> n >> k;
    vector<LL> a(n);
    for(auto &x: a) cin >> x;
    set<LL> b(a.begin(), a.end());
    LL ans = k * (k + 1) / 2;
    for(auto &x: b){
        if(x <= k) ans -= x;
    }
    cout << ans << endl;
    return 0;
}

D - Gomamayo Sequence

大意

给定一个01字符串S,对第i位取反需要 C_i的代价。

定义一个好的字符串,当且仅当只有一个相邻位置上的数字是相同的。

问将字符串变成好的字符串的最小代价。

思路

长度为n的好串只有n个,我们可以枚举所有的情况。

我们枚举相邻数字相同的位置,然后计算变成010101...101010...这两种情况的代价,取最小值。

可以设前缀和与后缀和数组pre_1,pre_2,suf_1,suf_2,事先计算所有前缀和后缀变成010101...101010...的代价,通过前缀代价+后缀代价,就可以得到当前枚举的情况的代价。

代码

#include<iostream>
#include<vector>
using namespace std;
#define int long long
typedef long long LL;
const int INF = 1e18 + 10;

template<class T>
T min(T a, T b, T c){
    return min(min(a, b), c);
}

signed main(){
    LL n;
    string s, t="01";
    cin >> n >> s;
    vector<LL> c(n);
    for(auto &x: c) cin>>x;
    vector<LL> pre1(n), pre2(n), suf1(n), suf2(n);
    for(int i = 0; i < n; i++){
        pre1[i] = (s[i] == t[i & 1]? c[i]: 0) + (i > 0? pre1[i - 1]: 0);
        pre2[i] = (s[i] == t[~i & 1]? c[i]: 0) + (i > 0? pre2[i - 1]: 0);
    }
    for(int i = n - 1; i >= 0; i--){
        suf1[i] = (s[i] == t[i & 1]? c[i]: 0) + (i < n - 1? suf1[i + 1]: 0);
        suf2[i] = (s[i] == t[~i & 1]? c[i]: 0) + (i < n - 1? suf2[i + 1]: 0);
    }
    LL ans = INF;
    for(int i = 0; i < n - 1; i++){
        ans = min(ans, pre1[i] + suf2[i + 1], pre2[i] + suf1[i + 1]);
    }
    cout << ans << endl;
    return 0;
}

E - Paint

大意

有一个h\times w的网格,初始所有格子都是颜色0,给定一些操作,每个操作是将一行或一列涂上指定颜色。执行完所有操作后,输出每种颜色的格子各有几个。(后涂的会覆盖先涂的)

思路

朴素的做法,最后统计颜色,时间复杂度O(hw)

考虑到每次操作都是对一行或一列涂色,其涂的格子数是已知的,所以可以直接累计结果。

但这样的问题是,由于后效性,后面的操作会影响到前面的结果,颜色会覆盖,导致先前涂的颜色数量可能会变少。

不妨将操作反过来考虑,先考虑最后一次操作,再考虑前一个操作,那么后考虑的操作不会影响先考虑的操作。

因此我们对操作倒过来考虑,每次操作所涂的格子数。格子数的求法比较简单。当然还要维护某一列某一行是否被涂过,后涂的操作是无效的。

注意颜色0也要算。

代码

#include<iostream>
#include<vector>
#include<array>
#include<algorithm>
#include<numeric>
using namespace std;
const int N=2e5+8;
#define int long long
typedef pair<int, int> PII;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);

    int h, w, m;
    cin >> h >> w >> m;
    vector<array<int, 3>> op(m);
    for(auto &[t, a, x]: op){
        cin >> t >> a >> x;
        a--;
    }

    vector<int> cnt(N, 0), used_row(h, 0), used_col(w, 0);
    int row = h, col = w;
    reverse(op.begin(), op.end());
    for (auto& [t, a, x]: op) {
        if (t == 1) {
            if (used_row[a]) continue;
            used_row[a] = 1;
            row--;
            cnt[x] += col;
        } else {
            if (used_col[a]) continue;
            used_col[a] = 1;
            col--;
            cnt[x] += row;
        }
    }
    cnt[0] += h * w - accumulate(cnt.begin(), cnt.end(), 0ll);
    vector<PII> ans;
    for (int i = 0; i < N; i++)
        if (cnt[i])
            ans.emplace_back(i, cnt[i]);
    
    cout << ans.size() << endl;
    for (auto& [i, c]: ans) cout << i << " " << c << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值