AtCoder Regular Contest 119 (ABC题)

这篇博客介绍了三道算法竞赛题目,涉及动态规划、字符串操作和区间更新问题。第一题通过枚举解决大数加法问题,第二题提出两种策略解决字符串转换的最小操作数,第三题利用奇偶位置高度特性解决楼房推平问题。通过这些实例,展示了在算法设计中观察和证明的重要性。
摘要由CSDN通过智能技术生成

比赛链接:Here

A - 119 × 2^23 + 1

注意到 \(2^{60} > 10^{18}\)​ ,所以我们可以直接枚举 \(0\) ~ \(59\)

int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    ll n; cin >> n;
    ll ans = (1ll << 60);
    for (int i = 0; i < 60; ++i) {
        ll a = n / (1ll << i);
        ll b = i;
        ll c = n - a * (1ll << i);
        ans = min(ans, a + b + c);
    }
    cout << ans;
}

B - Electric Board

给定长度为 \(n\) 两个字符串 \(S,T\)​,要求通过最少的操作数把 \(S\) 变成 \(T\),操作就是对于 \(s_l=0∧s_{l+1}=...=s_r=1\) 或者 \(s_l=1∧s_{l+1}=...=s_r=0\) 可以交换元素 \(s_l\)\(s_r\)

\(2≤n≤500000\)

解法1

我们可以把所有 \(0\) 换到应该的位置上,那么 \(1\) 也就确定了。

\(0\) 换过去的代价是路上 \(0\) 的数量,这就和 \(1\) 没关系了,那么我们把 \(S,T\)\(0\) 都取出来,相邻的配对即可。

解法2

我们可以把所以 \(1\) 换到应该的位置上,那么 \(0\) 也就是确定了。

\(1\) 换过去的代价是路上 \(0\) 的数量,这和 \(0/1\) 都有关系,直接匹配是行不通的,正确的做法是从左往右扫,如果遇到 \(S\)\(1\) 但是 \(T\) 没有就把他移动到右边第一个 \(0\),如果 \(T\)\(1\) 但是 \(S\) 没有也把他移动到右边第一个 \(0\)

这种做法的正确性有二:一是两个状态都往中间靠拢;二是我们永远在不得不操作的时候操作

int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    int n;
    string s, t;
    cin >> n >> s >> t;
    vector<int>a, b;
    for (int i = 0; i < n; ++i) {
        if (s[i] == '0') a.push_back(i);
        if (t[i] == '0') b.push_back(i);
    }
    if (int(a.size() != int(b.size()))) {cout << -1; return 0;}
    int ans = 0;
    for (int i = 0; i < int(a.size()); ++i)
        if (a[i] != b[i]) ans++;
    cout << ans;
}

C - ARC Wrecker 2

\(n\) 个楼房,第 \(i\) 个高为 \(a_i\),相邻的楼房可以同时增加或同时减少,问能够推平(高度全部变成 \(0\))的区间有多少个。

\(2≤n≤300000,1≤ai≤10^9\)

解法

一定要有敏锐的观察能力,这道题的结论是:如果奇偶位置高度相同则可以推平

证明不难,因为无论怎么操作奇偶的差都是不变的,而目标奇偶差值为 \(0\),初始状态一定能到目标状态。

然后搞一个特殊的前缀和,奇数位置符号为正,偶数位置符号为负,找权值和为 \(0\) 的区间即可。

const int N = 3e5 + 10;
ll n, ans, a[N];
map<ll, ll>mp;
int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        if (i > 1) a[i] += a[i - 2];
    }
    mp[0] = mp[a[1]] = 1;
    for (int i = 1; i <= n / 2; ++i) {
        int x = 2 * i;
        ans += mp[a[x - 1] - a[x]];
        if (x < n) ans += mp[a[x + 1] - a[x]];
        mp[a[x - 1] - a[x]]++;
        mp[a[x + 1] - a[x]]++;
    }
    cout << ans;
}

ll a[1 << 19], b[1 << 19], c[1 << 19];
map<ll, ll>d;
int main() {
    cin.tie(nullptr)->sync_with_stdio(false);
    ll n, ans = 0;
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= n; ++i) {
        if (i & 1) b[i] = a[i];
        else b[i] = -a[i];
        c[i] = c[i - 1] + b[i];
    }
    for (int i = 0; i <= n; ++i) {
        ans += d[c[i]];
        d[c[i]] += 1;
    }
    cout << ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值