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

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

比赛链接:Codeforces Round #768 (Div. 2)

A - Min Max Swap

题目大意:给出长度相同的a,b数组,你可以任意无限次的交换ai,bi(下标相同),问a数组最大乘b数组最大的结果最小能为多少。

解题思路:贪心思想,假设数组长度为n,那么在这2n个数里满足题目要求的情况是第1大的数*第n+1大的数,只需要将将所有的ai,bi比较,将较大的存入a数组,较小的存入b数组,则最后结果一定最小。

代码

#include<string>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 2e5+5;
int a[110],b[110],n;
void solve()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
        if(a[i]<b[i])swap(a[i],b[i]);
    }
    int maxx1=0,maxx2=0;
    for(int i=1;i<=n;i++)
    {
        maxx1 = max(maxx1,a[i]);
        maxx2 = max(maxx2,b[i]);
    }
    printf("%d\n",maxx1*maxx2);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)solve();
    return 0;
}

B-Fun with Even Subarrays

题目大意:给定长度为n的数组,你可以选定长度为k的区间,将此区间之前的相邻的长度为k的区间变成相同数字。例如 1,2,3,4,5选择区间[3,4]那么操作之后数组变成3,4,3,4,5 但你不能选择[2,3]因为之前相邻的数组长度小于K。问最小的操作次数能使数组所有数相同。

解题思路:可以观察到,只能由后面的数改变前面的数,所以最后所有相同的那个数一定等于a[n],因为如果不等于a[n],那么你将无法改变a[n].
所以我们可以选择从后向前染色,若数字相同则跳过,直到遇到不等于a[n]的数字,假设下标为id,那么可以选择区间[id+1,n]来操作,直到a[1]=a[n]

代码

#include<string>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 2e5+5;
int n,a[N];
void solve()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int ans = 0;
    for(int i=n;i>=2;i--)
    {
        if(a[i]==a[i-1])continue;
        int id = i;
        int len = n-i+1;
        ans++;
        for(int j=1;j<=len&&id-j>=1;j++)a[id-j] = a[n];
    }
    printf("%d\n",ans);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)solve();
    return 0;
}

接下来CD两题由于博主太菜,比赛的时候未做出,赛后看了两位大佬的讲解视频才懂,略作讲解。
大佬视频:
bilibili Maystern
bilibili eroengine

C-And Matching

题目大意:给定n(n是2的幂次)和k(0<=k<n),n个数为(0~n-1) 求解将其划分成n/2组,有a1&b1+a2&b2+……+an/2&bn/2=k

解题思路

可以观察到x&(n-1-x)=0  x&(n-1)=x
因为n是2的幂次,那么n-1二进制位上全是1
n=8 n=1000
n-1=7=0111
n-1减去x结果就是与按x位取反,
n-1&x的结果就是x
例如x=4=0100
n-1-x=0011 x&(n-1-x)=0

我们可以构造出这样的分组
0  n-1-0
1  n-1-1
2  n-1-2
……
n/2-1 n-n/2
对于每一对数&的结果都为0

1.于是我们可以将0与k对换位置
k&n-1 = k
0&n-1-k = 0
其他位置不变最终结果为所求K

2.若k = n-1需要采取另一种构造方法
n-1&n-2获得除2^0位上的所有1  res = n-2
再使1&n-1-2 获得2^0上的1   res = 1
以上两种分组之和为k,使0&2其他分组不变
注意若n=4 k=n-1 无解

代码

#include<string>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 1e5+5;
int n,k; 
int ans[N/2][2];
void solve()
{
    scanf("%d%d",&n,&k);
    if(n==4&&k==3)//唯一的无解
    {
        printf("-1\n");
        return ;
    }
    for(int i=0;i<=n/2-1;i++)//构造出所有组合&值都为0
    {
        ans[i][0] = i;
        ans[i][1] = n-1-i;
    }

    if(k==n-1)//特判
    {
        ans[0][0] = n-2;
        ans[1][1] = n-3;
        ans[2][1] = 0;
    }
    else
    {
        if(k<=n/2-1)swap(ans[0][0],ans[k][0]);
        else swap(ans[0][0],ans[n-1-k][1]);
    }

    for(int i=0;i<=n/2-1;i++)
        printf("%d %d\n",ans[i][0],ans[i][1]);
    
    return ;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)solve();
    return 0;
}

D-Range and Partition

题目大意:求最小的(y-x)[x,y]x,y为左右区间将长度为n的数组分成k段,使每一段在区间[x,y]内的数的数量严格大于区间外的。

解题思路:由题可知,k段中每一段在区间的数的数量至少比不在区间内的数的数量多1。并且只要整个数组满足整个条件那么一定存在将数组分成k段仍然满足的方法。判断条件 设整个数组中在区间内的数的数量为num,不在区间内的为n-num,只要数组,满足num-(n-num)>=k即可
于是我们枚举区间的左边界,二分求解满足条件的最小右边界,最后遍历数组遇到满意的区间直接输出即可

代码

#include<string>
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 2e5+5;
int n,k,a[N];
int vis[N];
/*
求最小的(y-x)[x,y]x,y为左右区间
将长度为n的数组分成k段,使每一段在区间[x,y]内的数的数量严格大于区间外的
由题可知,k段中每一段在区间的数的数量至少比不在区间内的数的数量多1
并且只要整个数组满足整个条件那么一定存在将数组分成k段仍然满足的方法
判断条件 设整个数组中在区间内的数的数量为num,不在区间内的为n-num
只要num-(n-num)>=k即可
于是我们枚举区间的左边界,二分求解满足条件的最小右边界,最后遍历数组遇到满意的区间直接输出即可
*/
bool check(int l,int r)
{
    int num = vis[r]-vis[l-1];
    return num-(n-num)>=k;
}
void solve()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)vis[i] = 0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        vis[a[i]]++;//计数
    }
    for(int i=1;i<=n;i++)vis[i]+=vis[i-1];//前缀和统计区间计数
    int x=1,y=n;
    for(int i=1;i<=n;i++)//枚举左边界
    {
        int l = i,r = n;
        int num = vis[r]-vis[l-1];
        if(num-(n-num)<k)break;//不存在以i为左边界的合法区间
        while(l<=r)//二分找到最小的合法右边界
        {
            int mid = l+r>>1;
            num = vis[mid]-vis[i-1];//区间范围内的数
            if(num-(n-num)>=k)r = mid - 1;
            else l = mid+1;
        }
        if(l-i<y-x)
        {
            x = i;
            y = l;
        }
    }
    printf("%d %d\n",x,y);
    for(int i=1,r=1;i<=n;i=r+1,r=i,k--)
    {
        if(k==1)
        {
            printf("%d %d\n",i,n);
            return ;
        }
        int cnt1=0,cnt2=0;
        while(true)
        {
            if(a[r]>=x&&a[r]<=y)cnt1++;
            else cnt2++;
            if(cnt1<=cnt2)r++;
            else break;
        }
        printf("%d %d\n",i,r);
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)solve();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值