Educational Codeforces Round 141 (Rated for Div. 2) A-C

Educational Codeforces Round 141 (Rated for Div. 2)


网址

A. Make it Beautiful

在这里插入图片描述

题目大意

如果一个数列 a a a 至少有一个元素等于在它之前的元素和,那么这个数列就是一个 u g l y ugly ugly 数列

如果数列不是 u g l y ugly ugly 数列,那么就是一个 b e a u t i f u l beautiful beautiful 数列

现在给你一个数列 a a a, 你可以随意修改这个数列中元素的数据,让它变成一个 b e a u t i f u l beautiful beautiful 数列

注意你不可以在数列中增加原本没有的数据,也不能删除存在的数据

如果有多种组合符合条件,你只需随意输出一个

思路/证明

这道题可以采用一个贪心的思维

我们把最大的放在前面,较小的放在后面,这样后面的元素就可以避免于之前的元素和相同了

值得注意的是,如果当前可以选的最大的数于之前元素的和一样,我们只需要寻找比它小的一个数就好了

求和的话可以用前缀和来维护

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long int lli;
lli total_ask, total_num;
const lli N = 100;
vector<lli> arr(N), sum(N), ans(N);
bool cmp(lli a, lli b){
    return a > b;
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> total_ask;
    while(total_ask--){
        cin >> total_num;
        arr.clear(); sum.clear(); ans.clear();
        arr.resize(N);sum.resize(N); ans.resize(N);//要重新resize,因为后面会将元素删除
        for(int temp = 1 ; temp <= total_num ; temp++)
            cin >> arr[temp];
        sort(arr.begin() + 1, arr.begin()+total_num+1, cmp);
        int flag = 1;
        for(int temp = 1 ; temp <= total_num ; temp++){
            int flag2 = 0;
            //下面的循环次数要进行变更,应为元素有删除
            for(int temp2 = 1 ,check = 0; temp2 <= total_num - temp + 1; temp2++){
                if(arr[temp2] != sum[temp - 1]){
                    ans[temp] = arr[temp2];
                    arr.erase(arr.begin()+temp2);
                    sum[temp] = sum[temp - 1] + ans[temp];
                    flag2 = 1;
                    break;
                }
            }
            if(!flag2) flag = 0;
        }
        if(!flag) cout << "No\n";
        else{
            cout << "YES\n";
            for(int temp = 1 ; temp <= total_num ; temp++)
                cout << ans[temp] << " ";
            cout << "\n";
        }
    }
}

B. Matrix of Differences

在这里插入图片描述

题目大意

对于一个矩阵 ( 1 2 3 4 5 6 7 8 9 ) \begin{pmatrix}1 &2&3\\ 4&5&6\\7&8&9\end{pmatrix} 147258369 , 拿元素5举例, 5 5 5 2 2 2 4 4 4 6 6 6 8 8 8相邻

将矩阵中的每一个数 a a a, 与其相邻的数 b b b 之间的差的绝对值 c = ∣ a − b ∣ c = |a-b| c=ab记录,我们可以得到一组数 C = c 1 , c 2 … c n C = {c_1,c_2\dots c_n} C=c1,c2cn

C C C中不同元素的个数为 v v v , 这个数 v v v 被称为这个矩阵的 b e a u t i f u l beautiful beautiful

现在给你一个整数 n n n,让你构建一个 n ⋅ n n\cdot n nn 的矩阵使得这个矩阵得 b e a u t i f u l beautiful beautiful 值最大

矩阵中的值范围为 [ 1 , n ⋅ n ] [1, n\cdot n] [1,nn] , 且矩阵中的元素只会出现一次

有多种组合,你只需要输出一种

思路/证明

这道题给我最大的触动是有些时候不要被样例带跑

我们拿 n = 3 n = 3 n=3 举例:

在这里插入图片描述

我们可以发现 b e a u t i f u l beautiful beautiful值最大其实就是 n 2 − 1 n^2 - 1 n21

所以我们只需要将上述这样一条“链子”找到,然后蛇形走位再将其排成一个矩阵即可

那我们如何将这条“链子”找出来呢

首先我们要的差值为 c c c, 当前数为 a a a, 那么下一个数有两种可能,一个是 a + c a+c a+c, 一个是 c − a c-a ca

如果这两个数有一个满足在 [ 1 , n ⋅ n ] [1,n\cdot n] [1,nn]中,且没有在之前出现过,即为答案

判断是否出现过可以使用set

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 50000;
vector<int>ans(N);
set<int>Check;
int arr[100][100];
int size_, use, total_ask;
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> total_ask;
    while(total_ask--){
        cin >> size_;
        ans.clear(); ans.resize(N);Check.clear();
        for(int temp = 2 ; temp <= size_*size_ ; temp++) Check.insert(temp);
        use = size_*size_ - 1;
        ans[0] = 1;
        for(int temp = 1 ; temp < size_*size_ ; temp++){
            int one = ans[temp-1]+use;
            int two = ans[temp-1]-use;
            if(one > 0 && one <= size_*size_){
                if(Check.count(one)){
                    ans[temp] = one;
                    Check.erase(one);
                }
            }
            if(two > 0 && two <= size_*size_){
                if(Check.count(two)){
                    ans[temp] = two;
                    Check.erase(two);
                }
            }
            use--;
        }
        int index_ = 0;
        //我是用另一个数组实现蛇形走位,牺牲了空间但比较好写,可以尝试控制输出端点位置以及方向来输出,这样更好
        for(int temp = 1 ; temp <= size_ ; temp++){
            if(temp % 2){
                for(int temp2 = 1 ; temp2 <= size_ ; temp2++)
                    arr[temp][temp2] = ans[index_++];
            }else{
                for(int temp2 = size_ ; temp2 >= 1 ; temp2--)
                    arr[temp][temp2] = ans[index_++];
            }
        }
        for(int temp = 1 ; temp <= size_ ; temp++){
            for(int temp2 = 1 ; temp2 <= size_ ; temp2++){
                cout << arr[temp][temp2] << " ";
            }
            cout << "\n";
        }
    }
    return 0;
}

C. Yet Another Tournament

在这里插入图片描述

题目大意

现在有 n n n 个人外加一个你, 这 n n n 个人的编号是 1 , 2 , 3 , … , n 1,2,3, \dots , n 1,2,3,,n

现在加上你一共有 n + 1 n+1 n+1个人,现在这 n + 1 n+1 n+1个人两两都要进行一场比赛

对于这 n n n个人来说,输赢很简单,两个人 a i a_i ai , a j a_j aj, 有且仅有 i > j i > j i>j 的时候 a i a_i ai 赢得比赛

但到了你,问题就变得复杂了,你有 m m m 的时间,这 n n n 个人都有一个时间 v v v,你需要准备 v v v 时间才能打败他

注意: 你在同一时间内只能准备一个人

思路/证明

注意:当你输一场的时候,你的对手的胜场将会+1,这可能会导致排名波动

现在假设你的胜场为 x x x ,其它人的胜场从 依次为 0 , 1 , 2 , 3 , … , n − 3 , n − 2 , n − 1 0,1,2,3,\dots,n-3,n-2,n-1 0,1,2,3,,n3,n2,n1

对于那些胜场不如你的人,即 0 , 1 , … , x − 1 0,1,\dots, x-1 0,1,,x1 的人,你与他们的输赢完全不会影响你的排名

对于那些胜场大于你的人,即 x , x + 1 , x + 3 , … , n − 3 , n − 2 , n − 1 x,x+1,x+3,\dots, n-3,n-2,n-1 x,x+1,x+3,,n3,n2,n1 的人,你与他们的输赢也不会影响你的排名

真正会影响你的排名的只有那个跟你胜场一样的人,你要是赢了它,你的排名就是他的排名,输了,你的排名就是它的下一位

明白了这个道理之后,我们再来考虑我们到底赢了多少场,即x是多少

x x x是单调的, x x x [ 0 , n ] [0,n] [0,n]中, 同理你的名次step也是单调的,在 [ 1 , n + 1 ] [1,n+1] [1,n+1]之中

所以我们可以二分答案来确定你的名词是多少(当然不二分也行)

我们从两个方向思考,一个是 x x x,即胜场

假设你赢了 t e m p temp temp 场, 那么你肯定超过了至少 t e m p temp temp人, 那么你的排名至少为 n − t e m p + 1 n - temp + 1 ntemp+1

现在假设你赢下 t e m p temp temp场的同时也赢下了那个与你一样胜场的人

也就是说你胜场为 t e m p − 1 temp-1 temp1的时候赢下了那个胜场为 t e m p temp temp的人,那么你的胜场将达到 t e m p temp temp,你的排名也会和他一样,变成 n − t e m p + 1 n - temp + 1 ntemp+1

具体的实现方法看代码2+注释

现在考虑名次 s t e p step step

假设你的名次是 s t e p step step,判断这个名次你是否可以达到,现在这个排名的胜场为 n − s t e p n - step nstep

达到的条件有两个

第一个是你的胜场已经超过了 n − s t e p n-step nstep

第二个你的胜场为 n − s t e p − 1 n-step-1 nstep1,但是你打败了原本那个在这个排名的人,你和它并列 s t e p step step

具体看代码1,(代码一不带注释,但是与代码二类似,可以参考代码二)懒…

代码

1.二分 //考虑名次

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6;
int info[N], sum[N], tem[N];
int total_ask, total_people, time_;
bool Check(int number){
    if(time_ >= sum[total_people - number + 1])
        return 1;
    if(info[total_people - number + 1] <= tem[total_people - number]){
        if(time_ >= sum[total_people - number])
            return 1;
    }else{
        if(time_ >= sum[total_people - number - 1] + info[total_people - number + 1])
            return 1;
    }
    return 0;
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> total_ask;
    while(total_ask--){
        cin >> total_people >> time_;
        for(int temp = 1 ; temp <= total_people ; temp++){
            cin >> info[temp];
            tem[temp] = info[temp];
        }
        sort(tem + 1, tem + total_people + 1);
        for(int temp = 1 ; temp <= total_people ; temp++)
            sum[temp] = tem[temp] + sum[temp - 1];
        int left = 1, right = total_people + 1;
        while(left < right){
            int mid = (left + right) >> 1;
            if(Check(mid)) right = mid;
            else left = mid + 1;
        }
        cout << left << "\n";
    }
    return 0;
}

2.枚举 //考虑胜场

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6;
int info[N], sum[N], tem[N];
int total_ask, total_people, time_;
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> total_ask;
    while(total_ask--){
        cin >> total_people >> time_;
        for(int temp = 1 ; temp <= total_people ; temp++){
            cin >> info[temp];
            tem[temp] = info[temp]; //拷贝一份原数组
        }
        sort(tem + 1, tem + total_people + 1);//对这个数组进行排序
        for(int temp = 1 ; temp <= total_people ; temp++)//对其求前缀和,目的是判断我所拥有的时间到底可以赢多少场
            sum[temp] = tem[temp] + sum[temp - 1];
        int ans = time_ < sum[1] ? total_people + 1 : total_people;//如果连第一场都赢不了,排名设为n+1
        for(int temp = 2 ; temp <= total_people ; temp++){//依次判断胜场数
            if(time_ >= sum[temp])//如果你可以胜temp场
                ans = total_people - temp + 1;//那么你的排名至少是这个
            if(info[temp] <= tem[temp - 1]){//当那个胜场为temp-1的人你在你可以打败的temp-1个人中时
                if(time_ >= sum[temp - 1]) //如果你可以打败temp-1的人
                    ans = total_people - temp + 1;//你的排名至少是这个
            }else{//当那个胜场为temp-1的人你不在在你可以打败的temp-1个人中时
                if(time_ >= pre[temp - 2] + info[temp])//你需要能打败temp-2个人外加那个胜场为temp-1的人
                    ans = total_people - temp + 1;
            }
        }
        cout << ans << "\n";
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yyym__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值