Codeforces Round 983 (Div. 2) 10.31 ABC题解

Codeforces Round 983 (Div. 2) 10.31 题解

A. Circuit

数学(math)贪心(greedy)模拟(implementation)

题意:

n盏灯,对应2n个开关,即每盏灯对应两个开关,开关打开或者关闭用10表示。
给出2n个开关的状态,需要求解出可能开灯的最小数量最大数量

输入:

第一行一个整数t,表示t组测试用例。(1t500)
每组第一行一个整数n,表示n盏灯。(1n50)
第二行2n个整数a1,a2,,a2n。表示开关的状态。(0ai1)

输出:

每组一行,两个整数,分别表示打开灯可能的最小数量和最大数量。

样例输入:

5
1
0 0
1
0 1
1
1 1
3
0 0 1 0 1 0
3
0 1 1 1 0 0

样例输出:

0 0
1 1
0 0
0 2
1 3

样例解释:

第一个样例中,开关都是关的,所以开灯最少最多都是0
第二个样例中,有一个开关是开的,所以这盏灯一定是亮的,所以开灯最少跟最多都是1
在第三个样例中,两个开关都是开的,表示一开一关,这个时候这盏灯是关着的,所以开灯最少跟最多都是0
在第四个样例中,有两个开关是打开的,如果他们对应同一盏灯,那么这个灯会熄灭,所以最少为0。如果不对应同一盏灯,就说明有两盏灯是亮着的,所以最多是2

分析:

我们发现,开关关闭对于结果并无影响。
所以首先需要统计开关打开的数量k
先求开灯最少数:
我们可以发现,当k是偶数个的时候,最小个数一定是0,因为两两对应,可以使得所有灯熄灭。
相反,如果k是奇数,他一定会余一个开关打开的状态,此时一定存在一盏灯是开着的,这时最少个数是1

再求最大值,我们可以得到,如果开kn,那么他们可以全开,例如样例五。
但是如果k>n我们可以分析到:一定存在一些灯开了一次然后又关了一次。这样会导致灯并不是全开,熄灭的灯会有 kn 盏灯,所以最后最多还剩下 2nk 盏灯亮着。

ac 代码:

#include <bits/stdc++.h>
using namespace std;
int T,n,m;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>T;
    while(T--){
        cin>>n;
        int cnt=0;
        for(int i=0;i<2*n;i++){
            cin>>m;
            cnt+=m;
        }
        int min_=0,max_=0;
        if(cnt%2)min_=1;
        else min_=0;
        if(cnt>n){
            max_=2*n-cnt;
        }
        else max_=cnt;
        cout<<min_<<" "<<max_<<endl;
    }
    return 0;
}

B. Medians

贪心(greedy)模拟(implementation)

题意:

给定一个整数nn是奇数)。他表示从1,2,,n的一个数组。然后再有一个整数k
我们需要将这个序列划分成奇数个序列,每个序列含有奇数个元素,并且每个序列的中位数组成的新序列的中位数等于k
中位数:数组排序后中间的元素,例如:序列([1,2,5,4,3])的中位数是3
这里定义的划分是连续元素的划分。
输出合理划分的子数组个数,以及每个子数组左端开头的元素。
如果不存在这样的划分,输出1

输入:

第一行一个整数t,表示t组测试用例。(1t5000)
每组数据一行两个整数nk(1kn2105)
输入限制:n是一个奇数。

输出:

每组两行,第一行一个整数nans,表示划分序列的个数。nans必须是奇数。
第二行nans个整数,每个整数表示ai序列的左端元素。

样例输入:

4
1 1
3 2
3 3
15 8

样例输出:

1
1
3
1 2 3
-1
5
1 4 7 10 13

样例解释:

第一个样例中,划分成的子数组只能是[1]
第二个样例中,可以化分成三个数组[1],[2],[3]
第三个样例中,我们可以证明,不管怎么划分,最后都不可能得到子数组中位数组成序列的中位数是3,所以无法实现,输出1
第四个样例中,可以划分出的子数组为[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]

分析:

由于此题存在无解的情况。我们首先考虑什么时候无解。
我们很容易想到,中位数定义是排序后中间的元素,所以不论如何划分,端点元素一定无法成为中位数。
所以如果是1或者n的时候一定无解。

再分析有解的情况:
我们可以很容易发现,我们划分数组可以分为三个,小于k,等于k,大于k
由于我们划分成的子数组必须是奇数,所以我们还需要考虑k奇偶问题。
我们很容易发现,k是偶数的话,他前面有奇数个元素,后面也有奇数个元素。
那么我们可以直接划分[1,k1][k][k+1,n]这样一定可以保证得到答案。

相反,如果k是奇数,我们很简单的思考到:我仅仅需要将中间划分的数组,中位数变为k即可。但是由于k是奇数,不能划分成一个。
我们很容易想到前面拿一个元素,后面也拿一个元素,这样前面一部分可以实现,后面一部分也同样可以实现了。

ac 代码:

#include <bits/stdc++.h>
using namespace std;
int T,n,k;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>T;
    while(T--){
        cin>>n>>k;
        
        if(n == 1){
            cout<<1<<endl;
            cout<<1<<endl;
        }
        else if(k==1||k==n){
            cout<<-1<<endl;
        }
        else{
            if(k%2==0){
                cout<<3<<endl;
                cout<<1<<" "<<k<<" "<<k+1<<endl;
            }
            else {
                cout<<3<<endl;
                cout<<1<<" "<<k-1<<" "<<k+2<<endl;
            }
            
        }
    }
    return 0;
}

C. Trinity

二分(binary search)双指针(two pointers)排序(sortings)

题意:

给定一个大小为n的数组a1,a2,,an
可以有如下操作,每次选择一个位置i,可以使ai转化成其他任意一个数组内的元素。

操作若干次后,需要让数组内任意三个元素大小满足一个三角形。即ax+ay>azay+az>axaz+ax>ay
求这样操作的最少次数。

输入:

第一行一个整数t,表示t组测试用例。(1t104)
每组第一行一个整数n(3n2105)
第二行n个整数ai(1ai109)

输出:

每组输出一个整数表示操作的最少次数。

样例输入:

4
7
1 2 3 4 5 6 7
3
1 3 2
3
4 5 3
15
9 3 8 1 6 5 3 8 2 1 4 2 9 4 7

样例输出:

3
1
0
8

样例分析:

第一个样例中,可以将数组首先变为[4,2,3,4,5,6,7],再变为[4,5,3,4,5,6,7],最后变成:[4,5,3,4,5,6,4],可以得到最少操作次数为3
第二个样例可以将数组变为[3,3,2]
第三个样例,三个元素已经可以构成三角形了,不需要操作。

分析:

我们发现数组内元素可以任意变化而不需要考虑位置,所以我们先排序。
我们对于有序数组,需要满足三个元素构成三角形,那么有i,j,k,一定会有ai+ak>ajaj+ak>ai,那么我们仅仅需要考虑ai+aj>ak即可,也就是前两个元素的和大于第三个元素
我们这时发现,我们仅仅需要考虑他变化的最少个数就是满足前两个元素的和大于第三个元素最大序列长度。
我们很容易得到这个长度可以通过二分来得到。

ac 代码:

#include <bits/stdc++.h>
using namespace std;
const int N=200010;
#define int long long
int a[N];
int T,n;
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    cin>>T;
    while(T--){
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        sort(a+1,a+n+1);
        int ans=0x3f3f3f3f3f3f3f3f;
        for(int i=1;i<n;i++){
            int tmp=a[i]+a[i+1];
            int l=i+1,r=n;
            int k=i;
            while(l<=r){
                int mid=l+r>>1;
                if(a[mid]<tmp){
                    k=mid;
                    l=mid+1;
                }
                else r=mid-1;
            }
            ans=min(ans,n-k+i-1);
        }
        cout<<ans<<endl;
    }
    return 0;
}

posted on   落雁单飞  阅读(93)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示