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

A

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

  • 因为两个数越靠近乘积越大,所以我们尽可能地让大数在 a a a数组中,让小数在 b b b数组中,然后分别取最大值乘积即可
#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

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

  • 显然应该从后往前覆盖,而且最后的数组的值一定等于最后一个数,所以可以记录当前长度从后往前遍历
#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] n n n个数分成两组,满足 ∑ a [ i ] & b [ i ] = k \sum a[i]\&b[i]=k a[i]&b[i]=k,保证 n n n 2 2 2的幂次, k ∈ [ 0 , n − 1 ] k\in[0,n-1] k[0,n1]

  • 我们首先知道,因为 n n n 2 2 2的幂次,所以在本题中,只要满足 a + b = n − 1 a+b=n-1 a+b=n1或者使用取反后再和n作与运算的方式,那么就有 a & b = 0 a\&b=0 a&b=0
  • 我们可以考虑一种构造方法,如果设 c ( k ) c(k) c(k) k k k构成二进制互补,我们只需要选择 0 , n − 1 , c ( k ) , k 0,n-1,c(k),k 0,n1,c(k),k这几个数即可实现题目要求,因为 0 & c ( k ) = 0 , ( n − 1 ) & k = k 0\&c(k)=0,(n-1)\&k=k 0&c(k)=0,(n1)&k=k,这样就满足了题目要求,且我们让其余的数都去找它们各自互补的数即可
  • 但是我们需要特判 k = 0 k=0 k=0 k = n − 1 k=n-1 k=n1,因为上述方案使用了这两个特殊值, k = 0 k=0 k=0时只需要让所有数都和与它互补的数字配对即可; k = n − 1 k=n-1 k=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 ≥ 4 n\geq4 n4,且 n = 4 n=4 n=4的时候发现 1 1 1使用了两次,所以进行一次特判,发现 n = 4 , k = 3 n=4,k=3 n=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

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

  • 假设我们已经知道了 x , y x,y x,y,那么我们怎么划分数组呢?枚举左端点,如果发现当前满足子数组要求,那么就让当前为一个子数组,换句话说,如果这个数组就一个数满足条件,那么就让这个数组里面就一个数,越少越好,这样的构造是合法的;如果当前这个数不满足条件,那么就继续往后走,直到数组中元素满足条件时再输出
  • 现在关键问题在于如何确定 x , y x,y x,y,首先我们来看一下至少要有多少个数在 [ x , y ] [x,y] [x,y]里面,设为 v a l val val,有 v a l ≥ n − v a l + k val\geq n-val+k valnval+k,那么 v a l ≥ n + k 2 val\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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Clarence Liu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值