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

比赛链接

A、Make it Beautiful

一种构造方法:按照从大到小的顺序构造,可以发现前缀和永远大于当前值。但是需要特判存在两个最大值的情况。只需要将最小值与第二位交换位置即可。最后暴力判一遍防止数字全部相等。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

bool cmp(int a, int b){
    return a > b; 
}

int a[100010]; 
int b[100010]; 

int main(){
    int T; 
    cin >> T;
    while(T--){
        int n; 
        cin >> n; 
        for(auto i = 1; i <= n; i++)
            cin >> a[i];
        sort(a + 1, a + n + 1, cmp); 
        if(a[1] == a[2]){
            b[1] = a[1]; 
            b[2] = a[n]; 
            for(int i = 3; i <= n; i++)
                b[i] = a[i-1]; 
        }
        else for(int i = 1; i <= n; i++) b[i] = a[i]; 
        int sum = 0; 
        bool flag = 1; 
        for(int i = 1; i <= n; i++){
            if(b[i] == sum){
                flag = 0; 
                break; 
            }
            sum += b[i]; 
        }
        if(flag){
            puts("YES"); 
            for(int i = 1; i <= n; i++)
                printf("%d ", b[i]); 
            cout << endl; 
        }
        else puts("NO"); 
    }
    return 0; 
}

B、Matrix of Differences

纯智商题。可以发现最大值一定是 \(n^2 - 1\) 种,即 \([1, n^2 - 1]\) 中的所有整数都会出现。于是考虑最大与最小挨着构造(只有这样才能出现差值为 \(n^2 - 1 、n^2 - 2 ......\) 的情况)。
那么考虑斜对角线交替放数,一条线从小到大,一条线从大到小,依次放入。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 100010

int a[51][51]; 

int main(){
    int T; cin >> T;
    while(T--){
        int n; cin >> n; 
        memset(a, 0, sizeof(a)); 
        a[1][1] = 1; 
        int del = 1;
        bitset <10010> vis(0);  
        int now = 1; int now2 = n * n; 
        for(int i = 1; i <= n; i++){
            int x = i, y = 1; 
            
            if(i % 2 == 0){
                while(x >= 1 && y <= n){
                    a[x][y] = now; 
                    now++; 
                    x--, y++; 
                }
            }
            else{
                while(x >= 1 && y <= n){
                    a[x][y] = now2; 
                    now2--;
                    x--, y++; 
                }
            }
        }

        for(int i = 2; i <= n; i++){
            int x = n, y = i; 
            
            if(i % 2 != (n % 2)){
                while(x >= 1 && y <= n){
                    a[x][y] = now; 
                    now++; 
                    x--, y++; 
                }
            }
            else{
                while(x >= 1 && y <= n){
                    a[x][y] = now2; 
                    now2--;
                    x--, y++; 
                }
            }
        }
        
        for(int i = 1; i <= n; i++, puts("")){
            for(int j = 1; j <= n; j++){
                printf("%d ", a[i][j]); 
            }
        }
    }
    return 0; 
}

C、Yet Another Tournament

(摘编自自己的洛谷题解)

题目大意

首先容易推出假如不算我们自己,那么剩下的敌人的得分就是数列 \(a_i = i\)。现在我们要做的就是花一定数量的时间来训练对应一部分敌人,打败他们同时自己获得一分(当然被打败的那个敌人就少获得一分)。

主要思路

容易想到我们肯定是想要自己的分尽量高,那么显然就是贪心,从小到大选择花费时间最小的敌人,一直到总和超过 \(m\) 为止。但存在一个问题,就是未被我们选中的敌人会增加 \(1\) 分。下面就此进行讨论。

假设我们按照贪心的方法选择了 \(k\) 个敌人进行训练,那么容易知道 \(k\) 就是我们自身的最大得分。假设有一敌人的初始得分大于 \(k\), 那我们完全就可以忽略他。因为显而易见的是无论我们如何“努力”,我们都不可能战胜他。对于初始得分小于 \(k\) 的敌人,显然排名永远会落后我们。所以,我们只需要考虑剩下一个唯一情况,就是初始得分等于 \(k\) 的人。假设我们在保持总分不变的情况下没有选择战胜他,那么我们就会损失一个排名。所以尝试将该人与最大值进行交换,尽可能选中他。

正确性

下面简略说一下上述方法的正确性(最优性)。假设我们的得分为 \(k\), 那么我们能战胜的敌人就是 \(k\) 个(不算临界情况)。如果我们能将临界者战胜,就是 \(k+1\) 个。如果为了加入当前临界者而导致 \(k\) 减少一,显然也不优。所以最大化 \(k\) 并尽量拉入临界者是最优做法。

代码时间

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 500010

int n, m; 

struct node{
    int w, id; 
} t[N]; 
int a[N]; 
int b[N];

bool cmp1(node a, node b){
    return a.w < b.w; 
}

int main(){
    int T; cin >> T; 
    while(T--){
        cin >> n >> m; 
        for(int i = 1; i <= n; i++){
            cin >> t[i].w;
            t[i].id = i; 
            a[i] = t[i].w; 
        } 
        sort(t + 1, t + n + 1, [](node a, node b){
            return a.w < b.w; 
        }); 
        bitset <N> vis(0); 
        int sum = 0, maxn = n, ans = 0; 
        for(int i = 1; i <= n; i++){
            if(sum + t[i].w > m){
                maxn = i-1; 
                break; 
            } 
            sum += t[i].w;
            vis[t[i].id] = 1; 
            ans++; 
        }
        if(!vis[ans+1]){
            if(sum - t[maxn].w + a[ans+1] <= m){
                vis[ans+1] = 1;
                vis[t[maxn].id] = 0; 
            }
        }
        for(int i = 1; i <= n; i++)
            b[i] = i - 1 + (!vis[i]);
        b[n+1] = ans; 
        sort(b + 1, b + n + 2, [](int a, int b) -> bool {return a > b; });  // 排序计算排名
        int num = 0; 
        for(int i = 1; i <= n + 1; i++){
            num++; 
            if(b[i] == ans){
                break; 
            }
        }
        cout << num << endl; 
    }
    return 0; 
}
posted @ 2023-01-09 16:49  雪之下,树之旁  阅读(79)  评论(0)    收藏  举报