栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Codeforces Round #768(Div.2)(补题)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Codeforces Round #768(Div.2)(补题)

A-Min Max Swap

题意:给出两个数组,通过调换两个数组相同位置的数字,使得两个数组的最大值的成绩最小。

思路:两个数的乘积最小,所以尽量使两个数相差较大。同时遍历两个数组,比较两个数组同一个位置上的两个数,每次使大的数在a数组,小的数在b数组,并同时将两个数组的最大值找出来,这个最大值的乘积就是最小的乘积。

代码:

#include 

using namespace std;

int main()
{
    int t;
    cin >> t;
    while(t--){
        int n, a[105], b[105];
        cin >> n;
        for(int i = 0; i < n; ++i){
            cin >> a[i];
        }
        for(int i = 0; i < n; ++i){
            cin >> b[i];
        }
        int max1 = 0, max2 = 0;
        for(int i = 0; i < n; ++i){
            max1 = max(max1, max(a[i], b[i]));//寻找a数组的最大值
            max2 = max(max2, min(a[i], b[i]));//寻找b数组的最大值
        }
        cout << max1 * max2 << endl;
    }
    return 0;
}
B-Fun with Even Subarrays

题意:选择一个大小为2k的子区间,用子区间内后面k个数替代前面k个数,多次使用该操作,使得所给的数组中所有数字都相同。

思路:由题目中所给的操作不难发现,不论在什么情况下,数组的最后一个数字都没有办法改变,所以我们只能将所有的数都转化成与最后一个数相等。故令k = 1开始遍历,若 a n − 1 − k = a n − 1 a_{n - 1 - k}= a_{n - 1} an−1−k​=an−1​,则i继续左移直到第一个值不等于ai的数,然后执行一次操作,并令k = 2 * k,直到k >= n。

代码:

#include 
 
using namespace std;
int a[200005];
 
int main()
{
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        for(int i = 0; i < n; ++i){
            cin >> a[i];
        }
        int k = 1, cnt = 0;
        while(k < n){
            while(a[n - 1 - k] == a[n - 1]){
                k++;
            }
            if(k < n){
                k *= 2;
                ++cnt;
            }
        }
        cout << cnt << endl;
    }
    return 0;
}
C-Add Matching

题意:将0~n-1中的数字分成n/2对,使得所有对的按位与之和等于k。

思路:找到题目中每一个数字对应的按位与为0的那个数c(x),即c(x)=x⊕(n−1) ,其中n−1=11…11(二进制),然后根据k的大小分情况讨论:
当k = 0时:
就按照顺序从i = 0开始输出i和c(i)。
当0 < k < n - 1时:
不难发现k & (n - 1) = k,0 & c(k) = 0,然后使其他数x与对应的c(x)组成一队,这样就使得最终所有数对的按位与之和等于k了。
当k = n - 1时:
有(n - 2) & (n - 1) = (n - 2);
(n - 3) & 1 = 1;
0 & 2 = 0;
所以使得以上三组各位一队时,按位与之和为n - 1,然后使其他数x与c(x)成一对。
但是这种情况下至少要有3对,又因为n总是2 的幂,所以n >= 8时才有解,当n = 4时无解。

代码:

#include 

using namespace std;

int main()
{
    int t;
    cin >> t;
    while(t--){
        int n, k;
        cin >> n >> k;
        if(n <= 4 && k == n - 1){
            cout << -1 << endl;
        }
        else if(k == 0){
            for(int i = 0 ; i < (i ^ (n - 1)); ++i){
                cout << i << " " << (i ^ (n - 1)) << endl;
            }
        }
        else if(k == n - 1 && n > 4){
            for(int i = 0; i < (i ^ (n - 1)); ++i){
                //cout << (i ^ (n - 1)) << endl;
                if(i == 0)
                    cout << 0 << " " << 2 << endl;
                else if(i == 2)
                    continue;
                else if(i == 1)
                    cout << 1 << " " << n - 3 << endl;
                else
                    cout << i << " " << (i ^ (n - 1)) << endl;
            }
            cout << n - 2 << " " << n - 1 << endl;
        }
        else{
            for(int i = 0; i < (i ^ (n - 1)); ++i){
                if(i == 0)
                    cout << i << " " << (k ^ (n - 1)) << endl;
                else if(i == k || i == (k ^ (n - 1)))
                    cout << k << " " << n - 1 << endl;
                else{
                    cout << i << " " << (i ^ (n - 1)) << endl;
                }
            }
        }
    }
    return 0;
}

D-Range And Partition

题意:将所给数组分为k个子数组,寻找一个最小区间[x, y],使得所得到的k个数组中,处于该区间的数的数量多余不处于该区间的数的数量。

思路:利用前缀和的思想,如果索引为i的数在该区间内,则 b i b_i bi​的值为1,反之则为-1,那么数组b的前缀和 s u m i sum_i sumi​是否大于0,则说明前i位数中处于区间内的数是多还是少,那么进一步 s u m r − s u m l − 1 sum_r - sum_{l - 1} sumr​−suml−1​的值表示的就是在区间[l, r]中,在区间内的数比不在区间内的数多的个数。
①先找到这个最小的区间[x, y]
由前缀和的思想可得:
∑ i = 1 n b i ≥ k displaystylesum_{i=1}^{n}b_i geq k i=1∑n​bi​≥k
∑ i = 1 n ( − 1 + 2 ∗ [ x ≤ a i ≤ y ] ) ≥ k displaystylesum_{i=1}^{n}({-1 + 2 * [xleq a_i leq y]} )geq k i=1∑n​(−1+2∗[x≤ai​≤y])≥k
∑ i = 1 n [ x ≤ a i ≤ y ] ≥ k + n 2 displaystylesum_{i=1}^{n}[xleq a_i leq y] geq {frac{k + n }{2}} i=1∑n​[x≤ai​≤y]≥2k+n​(取上限)
所以将数组排序之后遍历即可找到这个最小的区间。
②将区间分成k个子区间
然后计算 s u m r − s u m l − 1 sum_r - sum_{l - 1} sumr​−suml−1​的值,从第一个数开始遍历,当遇到第一个索引r满足条件时输出,然后l置为r + 1,依次列举出分割的数组。

代码:

#include 

using namespace std;

int main()
{
    int t;
    cin >> t;
    while(t--){
        int n, k, a[200005], sorted_a[200005];
        cin >> n >> k;
        for(int i = 0; i < n; ++i){
            cin >> a[i];
            sorted_a[i] = a[i];
        }
        sort(sorted_a, sorted_a + n);
        
        int min = INT_MAX, len = (k + n + 1) / 2, x = 1, y = n;
        for(int i = 0; i + len - 1 < n; ++i){
            if(min > sorted_a[i + len - 1] - sorted_a[i]){
                min = sorted_a[i + len - 1] - sorted_a[i];
                x = sorted_a[i];
                y = sorted_a[i + len - 1];
                //cout << x << " " << y << endl;
            }
        }
        cout << x << " " << y << endl;
        int sum = 0, count = 1, pos = 0;
        for(int i = 0; i < n; ++i){
            if(x <= a[i] && a[i] <= y)
                ++sum;
            else
                --sum;
            if(sum > 0 && count < k){
                cout << pos + 1 << " " << i + 1 << endl;
                pos = i + 1;
                ++count;
            }
        }
        cout << pos + 1 << " " << n << endl;
    }
    return 0;
}

这次的比赛其实还是只做出来了前面的两道题,卡在了第三题上面,想到了寻找按位与为0的组队,但是没有想到对应的数就是与 n − 1 n - 1 n−1的异或,所以思路就卡住了,但是就算这一步想到了,也很难想到后面的;第四道题的话,最开始想的时候大致思路与题解一样,但是在实现的时候还是没有想到前缀和推导求[x, y]的范围,把处于区间内的数组之和误当成了大于等于 n 2 frac{n}{2} 2n​,还是要多学会用数学推导求范围。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/723237.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号