Codeforces Round #768 (Div. 2) A - D

本文解析四道算法竞赛题目,包括数组操作最小化问题、数组覆盖转换问题、位运算分组问题及数组划分优化问题,提供了详细的思路与代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A

随意交换a[i]a[i]a[i]b[i]b[i]b[i],问max{a[i]}×max{b[i]}max\{a[i]\}\times max\{b[i]\}max{a[i]}×max{b[i]}的最小值

  • 因为两个数越靠近乘积越大,所以我们尽可能地让大数在aaa数组中,让小数在bbb数组中,然后分别取最大值乘积即可
#include <bits/stdc++.h>

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector<int> a(n), b(n);
        for(auto &i : a) cin >> i;
        for(auto &i : b) cin >> i;
        int mxa = -1;
        int mxb = -1;
        for(int i=0;i<n;i++){
            if(a[i] < b[i]) swap(a[i], b[i]);
            mxa = max(mxa, a[i]);
            mxb = max(mxb, b[i]);
        }
        cout << mxa * mxb << '\n';
    }
    return 0;
}

B

给你一个数组,现在可以进行一种操作,用后面的连续kkk项覆盖之前的连续kkk项,问将这个数组变成同一个数所需要的最少操作数

  • 显然应该从后往前覆盖,而且最后的数组的值一定等于最后一个数,所以可以记录当前长度从后往前遍历
#include <bits/stdc++.h>

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector<int> a(n + 1);
        for(int i=n;i>=1;i--) cin >> a[i];
        int ans = 0;
        int r = 1;
        for(int i=1;i<=n;i++){
            if(a[i] != a[1]){
                while(i > r){
                    r *= 2;
                    ans += 1;
                }
            }else{
                r = max(r, i);
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

C

让你把[0,n−1][0,n-1][0,n1]nnn个数分成两组,满足∑a[i]&b[i]=k\sum a[i]\&b[i]=ka[i]&b[i]=k,保证nnn222的幂次,k∈[0,n−1]k\in[0,n-1]k[0,n1]

  • 我们首先知道,因为nnn222的幂次,所以在本题中,只要满足a+b=n−1a+b=n-1a+b=n1或者使用取反后再和n作与运算的方式,那么就有a&b=0a\&b=0a&b=0
  • 我们可以考虑一种构造方法,如果设c(k)c(k)c(k)kkk构成二进制互补,我们只需要选择0,n−1,c(k),k0,n-1,c(k),k0,n1,c(k),k这几个数即可实现题目要求,因为0&c(k)=0,(n−1)&k=k0\&c(k)=0,(n-1)\&k=k0&c(k)=0,(n1)&k=k,这样就满足了题目要求,且我们让其余的数都去找它们各自互补的数即可
  • 但是我们需要特判k=0k=0k=0k=n−1k=n-1k=n1,因为上述方案使用了这两个特殊值,k=0k=0k=0时只需要让所有数都和与它互补的数字配对即可;k=n−1k=n-1k=n1的时候,我们可以选择这样几组特殊值(n−1,n−2),(n−3,1),(0,2)(n-1,n-2),(n-3,1),(0,2)(n1,n2),(n3,1),(0,2),这里发现n≥4n\geq4n4,且n=4n=4n=4的时候发现111使用了两次,所以进行一次特判,发现n=4,k=3n=4,k=3n=4,k=3时无解,按照上述构造方法只有这一种情况无解,然后其余选择与它本身配对的
  • 此题很巧妙
#include <bits/stdc++.h>

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n, k;
        cin >> n >> k;
        if(n == 4 && k == 3){
            cout << -1 << '\n';
            continue;
        }
        if(k == 0){
            vector<bool> vis(n + 1);
            for(int i=0;i<n;i++){
                int now = n - 1 - i;
                if(vis[i] || vis[now]) continue;
                cout << i << ' ' << now << '\n';
                vis[i] = vis[now] = true;
            }
        }else if(k == n - 1){
            cout << n - 1 << ' ' << n - 2 << '\n';
            cout << n - 3 << ' ' << 1 << '\n';
            cout << 0 << ' ' << 2 << '\n';
            vector<bool> vis(n + 1);
            vis[n - 1] = vis[n - 2] = vis[n - 3] = vis[1] = vis[0] = vis[2] = true;
            for(int i=0;i<n;i++){
                int now = n - 1 - i;
                if(vis[now] || vis[i]) continue;
                cout << now << ' ' << i << '\n';
                vis[now] = vis[i] = true;
            }
        }else{
            cout << 0 << ' ' << n - 1 - k << '\n';
            cout << n - 1 << ' ' << k << '\n';
            vector<bool> vis(n + 1);
            vis[0] = vis[n - 1] = vis[n - 1 - k] = vis[k] = true;;
            for(int i=0;i<n;i++){
                int now = n - 1 - i;
                if(vis[now] || vis[i]) continue;
                cout << i << ' ' << now << '\n';
                vis[i] = vis[now] = true;
            }
        }
    }
    return 0;
}

D

让你把一个数组分成kkk个子数组,让你规定一个范围[x,y][x,y][x,y],使得每个子数组中的元素值在这个范围里的元素数量严格大于在这个范围之外的元素数量,现在要让y−xy-xyx尽可能的小,现在求这个范围还有划分若干子数组的方案

  • 假设我们已经知道了x,yx,yx,y,那么我们怎么划分数组呢?枚举左端点,如果发现当前满足子数组要求,那么就让当前为一个子数组,换句话说,如果这个数组就一个数满足条件,那么就让这个数组里面就一个数,越少越好,这样的构造是合法的;如果当前这个数不满足条件,那么就继续往后走,直到数组中元素满足条件时再输出
  • 现在关键问题在于如何确定x,yx,yx,y,首先我们来看一下至少要有多少个数在[x,y][x,y][x,y]里面,设为valvalval,有val≥n−val+kval\geq n-val+kvalnval+k,那么val≥n+k2val\geq \frac{n+k}{2}val2n+k,根据元素数量进行枚举左右端点,从而最小化区间长度
  • 参考T神代码,写的实在漂亮
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n, k;
        cin >> n >> k;
        vector<int> a(n);
        for(int i=0;i<n;i++) cin >> a[i];
        auto b = a;
        sort(b.begin(), b.end());
        int in = (n + k + 1) / 2;
        int x = -1;
        int y = n + 1;
        for(int i=0;i<=n-in;i++){
            int L = b[i];
            int R = b[i + in - 1];
            if(R - L < y - x){
                x = L;
                y = R;
            }   
        }
        cout << x << ' ' << y << '\n';
        int mx = -1;
        int cnt = 0;
        int last = -1;
        for(int i=0;i<n;i++){
            cnt += ((x <= a[i] && a[i] <= y) ? 1 : -1);
            if(cnt > mx){
                mx = cnt;
                if(cnt >= 1 && cnt < k){
                    cout << last + 2 << ' ' << i + 1 << '\n';
                    last = i;
                }
            }
        }
        cout << last + 2 << ' ' << n << '\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值