[补题] Codeforces Round 892 (Div. 2)

Codeforces Round 892 (Div. 2)

感觉被诈骗了ww

A

Translate & Description

你有一个\(a\)数组,你可以将\(a\)数组的每一个数放到\(b\)数组或者\(c\)数组中。构造完后你需要确保\(\forall b_i\)%\(\forall c_j \ne 0(i\in [1,n],j\in[1,m])\) (我们规定\(n\)\(a\)数组的大小,\(m\)\(b\)数组的大小。数组下标均从\(1\)开始)。

输出共三行,第一行两个整数,分别为\(n\)\(m\),显然你需要保证\(n+m=size(a)\)\(size(a)\)为初始\(a\)数组的大小)

第二行\(n\)个整数,即你构造的\(b\)数组

第三行\(m\)个整数,即你构造的\(c\)数组

本题开启spj,你的构造只要符合题意都判定为正确而无需和stdoutput一致。
%我不会打latex就直接打了ww

Analysis

一道非常CF的构(zha)造(pian)题

我们需要保证我们构造出来的\(b,c\)数组中\(\forall c_i\)不是\(\forall b_i\)的除数。也就是不能整除。

我们是不是只要确保\(\forall c_i > \forall b_i\)就可以啦?

由此,我们可以直接判断出无解情况:当且仅有\(a\)数组所有数都相同时,无解。

反证:假设\(a\)数组中并不是所有数都不相同,则\(a\)数组中存在最大值。我们可以把不等于数组最大值的数字放到\(b\)数组中,反之等于最大值的数字放到\(c\)数组中,这样显然可以确保\(\forall c_i > \forall b_i\)。构造符合题意。

上述反证即为具体操作过程。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 20010;
int T;
int n;
int a[N];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        int pos = 0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        sort(a+1,a+n+1);
        if(a[1] == a[n]) 
        {
            cout<<"-1"<<endl;
            continue;
        }
        int cnt1 = 0;
        for(int i=1;i<=n;i++)
        {
            if(a[i] != a[n]) 
            {
            //    cout<<a[i]<<" ";
                cnt1 ++;
            }
            else 
            {
                pos = i;
                break;
            }
        }
        cout<<cnt1<<" "<<n-cnt1<<endl;
        for(int i=1;i<=n;i++)
        {
            if(a[i] != a[n]) 
            {
                cout<<a[i]<<" ";
            //    cnt1 ++;
            }
            else 
            {
                pos = i;
                break;
            }
        }
        cout<<endl;
        for(int i=pos;i<=n;i++) cout<<a[i]<<" ";
        cout<<endl;
    }
}

B

Translate & Description

你有\(m\)个数组,你可以对每次数组进行一次操作,当然也可以不操作,操作如下:

  • 将该数组的一个数移动到另一个数组中

注意,你可以对每个数组都进行一次这样的操作,而不是只进行一次操作(当时比赛理解错题意了qwq)

所有操作完成后,求每个数组的最小值和最大。形式化地,求\(max( \sum_{i=1}^n \min_{j=1}^{m_i} a_{i,j} )\)

Analysis

不妨考虑什么数字会对答案产生贡献。

首先,也是最容易想到的是,\(m\)个数组中所有数的最小值一定会产生贡献,因为无论怎么移动她都是一个数组的最小值。

接下来考虑移动。

首先一个结论,能够对答案产生贡献的只有每个数组的最小值和次小值

简单理解一下:每个数组要么移动,要么不移动。如果移动只有移动最小值才对该数组的最小值产生贡献,那么此时产生贡献的是次小值。如果不移动那么产生贡献的是最小值。因为一个数组最多只能进行一次操作。所以最优策略是移动一个数组的最小值,这样才能对该数组的MINN产生正贡献,而这样的操作使得一个数组的次小值对答案产生贡献。

而如果我们把一个数组中不是最小值的数移动到另一个数组,不仅不会改变当前数组的最小值,还不会改变另一个数组的最小值,具体证明如下:

定义移动过来的数是\(x\),原来该数组的minn 为\(y\)

  • 若$ x > y $则不会产生任何贡献
  • \(x < y\) 则会产生负贡献
  • \(x = y\) 则显然不会有贡献

由此看来我们如果移动一个数组中不是MINN的数,要么没有贡献,要么产生负贡献,我们显然不会去这么做。

而我们想要利益最大化可以将每个数组都移动。

如何移动呢?

前面提到\(m\)个数组中所有数的最小值一定会产生贡献,我们可以把她单独放到一个数组中,再把所有数组的最小值放到这个数组中,这样对答案产生贡献的就是所有数字的最小值+除了所有数字最小值所在数组其他所有数组的次小值。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 20010;
int T;
int n;
int a[N];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        int pos = 0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        sort(a+1,a+n+1);
        if(a[1] == a[n]) 
        {
            cout<<"-1"<<endl;
            continue;
        }
        int cnt1 = 0;
        for(int i=1;i<=n;i++)
        {
            if(a[i] != a[n]) 
            {
            //    cout<<a[i]<<" ";
                cnt1 ++;
            }
            else 
            {
                pos = i;
                break;
            }
        }
        cout<<cnt1<<" "<<n-cnt1<<endl;
        for(int i=1;i<=n;i++)
        {
            if(a[i] != a[n]) 
            {
                cout<<a[i]<<" ";
            //    cnt1 ++;
            }
            else 
            {
                pos = i;
                break;
            }
        }
        cout<<endl;
        for(int i=pos;i<=n;i++) cout<<a[i]<<" ";
        cout<<endl;
    }
}

UPD:2023/8/15

熬不住了改天再更

posted @   SXqwq  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示