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

Codeforces Round 983 (Div. 2) 10.31 题解

A. Circuit

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

题意:

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

输入:

第一行一个整数\(t\),表示\(t\)组测试用例。\((1 \leq t \leq 500)\)
每组第一行一个整数\(n\),表示\(n\)盏灯。\((1 \leq n \leq 50)\)
第二行\(2 \ast n\)个整数\(a_1,a_2,\ldots,a_{2 \ast n}\)。表示开关的状态。\((0 \leq a_i \leq 1)\)

输出:

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

样例输入:

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\)

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

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)

题意:

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

输入:

第一行一个整数\(t\),表示\(t\)组测试用例。\((1 \leq t \leq 5000)\)
每组数据一行两个整数\(n\)\(k\)\((1 \leq k \leq n \leq 2 \ast 10^5)\)
输入限制:\(n\)是一个奇数。

输出:

每组两行,第一行一个整数\(n_ans\),表示划分序列的个数。\(n_ans\)必须是奇数。
第二行\(n_ans\)个整数,每个整数表示\(a_i\)序列的左端元素。

样例输入:

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,k-1]\)\([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\)的数组\(a_1,a_2,\ldots, a_n\)
可以有如下操作,每次选择一个位置\(i\),可以使\(a_i\)转化成其他任意一个数组内的元素。

操作若干次后,需要让数组内任意三个元素大小满足一个三角形。即\(a_x + a_y > a_z\)\(a_y + a_z > a_x\)\(a_z + a_x > a_y\)
求这样操作的最少次数。

输入:

第一行一个整数\(t\),表示\(t\)组测试用例。\((1 \leq t \leq 10^4)\)
每组第一行一个整数\(n\)\((3 \leq n \leq 2 \ast 10^5)\)
第二行\(n\)个整数\(a_i\)\((1 \leq a_i \leq 10^9)\)

输出:

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

样例输入:

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\),一定会有\(a_i+a_k>a_j\)\(a_j+a_k>a_i\),那么我们仅仅需要考虑\(a_i+a_j>a_k\)即可,也就是前两个元素的和大于第三个元素
我们这时发现,我们仅仅需要考虑他变化的最少个数就是满足前两个元素的和大于第三个元素最大序列长度。
我们很容易得到这个长度可以通过二分来得到。

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 2024-11-04 17:12  落雁单飞  阅读(70)  评论(0编辑  收藏  举报

导航