题解 | #比赛安排(PDF题面存放于本题)#

比赛安排(PDF题面存放于本题)

https://ac.nowcoder.com/acm/contest/120562/A

这里是A,B,I,F,H,E的个人想法

A

对于任意连续的3场比赛的类型互不相同,即三种比赛当且仅当以abcabc的方式进行,换句话说,最少和最多的比赛场次不能差超出1

代码展示

#include<bits/stdc++.h>
using namespace std;
int main(){
    int T;
    cin>>T;
    while(T--) {
        long long a,b,c;
        cin>>a>>b>>c;
        long long m=max({a, b, c});
        long long n=min({a, b, c});
        if (m<=n+1){
            cout<<"Yes\n";
        }else{
            cout<<"No\n";
        }
    }
    return 0;
}

B

获胜的唯一条件:成为「严格唯一」的最大值

游戏规则的本质是:

任意两个不同值对决,值大的留下、小的淘汰;值相等的对决,两者都淘汰。 推导结论:

1.如果选手的美味值不是数组最大值 → 一定会在某一轮被最大值选手淘汰,无法获胜;

2.如果最大值出现≥2 次 → 任意两个最大值对决会双双淘汰,最终无选手存活,所有位置都无法获胜;

3.只有最大值「仅出现 1 次」时,这个最大值选手可以通过 “逐个淘汰其他选手” 的策略最终获胜,其余选手都无法获胜。

代码展示

using namespace std;
int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        vector<long long>a(n);
        long long maxi=LLONG_MIN;
        int m1= 0;
        int m2= -1;
        for(int i=0; i<n; i++){
            cin >> a[i];
            if(a[i] > maxi){
                maxi=a[i];
                m1= 1;
                m2= i;
            }else if(a[i] == maxi){
                m1++;
            }
        }
        string res(n, '0');
        if(m1== 1){
            res[m2] = '1';
        }
        cout<<res<<'\n';
    }
    return 0;
}

I

对于每个位置 (i,j),只要存在一个相邻的格子(上下左右)和它的数字相同,就一定能构造出满足条件的回文路径(比如走两步:(i,j) → (x,y) → (i,j) 虽然是简单路径,但终点是起点,所以我们需要找一个非起点的位置。其实更简单的是:只要存在两个相邻的相同数字,那么从这两个数字中的任意一个出发,都可以直接走到另一个,形成长度为 2 的回文串,比如 "00" 或 "11",这显然是回文的)。

所以,一个位置 (i,j) 输出 Y 的充要条件是:

1.它的四个相邻方向中,至少有一个格子的数字和它相同;

2.或者,整个矩阵中至少有两个和它相同的数字,并且这两个数字在矩阵中是连通的(在 01 矩阵里,只要数量 ≥2 就一定能找到路径,因为矩阵是连通的网格)。

思路:

1.先遍历整个矩阵,标记每个位置 (i,j) 是否有相邻的相同数字。

2.如果一个位置有相邻的相同数字,那么它的答案就是 Y。

3.如果一个位置没有相邻的相同数字,再看:

如果矩阵中至少有 2 个和它相同的数字,那么答案是 Y(因为可以绕路走到另一个相同数字,形成长度 ≥2 的回文串)。
如果矩阵中只有 1 个和它相同的数字,那么答案是 N。

代码展示

#include <bits/stdc++.h>
using namespace std;
int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, m;
        cin >> n >> m;
        vector<string> a(n);
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        vector<vector<bool>> has_same(n, vector<bool>(m, false));
        int dx[] = {-1, 1, 0, 0};
        int dy[] = {0, 0, -1, 1};
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                for (int d = 0; d < 4; d++) {
                    int ni = i + dx[d];
                    int nj = j + dy[d];
                    if (ni >= 0 && ni < n && nj >= 0 && nj < m) {
                        if (a[i][j] == a[ni][nj]) {
                            has_same[i][j] = true;
                            break;
                        }
                    }
                }
            }
        }
        int cnt0 = 0, cnt1 = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (a[i][j] == '0') cnt0++;
                else cnt1++;
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (has_same[i][j]) {
                    cout << 'Y';
                } else {
                    if (a[i][j] == '0' && cnt0 >= 2) {
                        cout << 'Y';
                    } else if (a[i][j] == '1' && cnt1 >= 2) {
                        cout << 'Y';
                    } else {
                        cout << 'N';
                    }
                }
            }
            cout << '\n';
        }
    }
    return 0;
}

F

利用数论中 “相邻整数互质” 的核心性质,构造满足 gcd(x,y)=n 的两个数即可

代码展示

#include <bits/stdc++.h>
using namespace std;
int main() {
    const long long BASE = 1LL << 31;
    const long long BASE_PLUS_1 = BASE + 1;
    int t;
    cin >> t;
    while (t--) {
        long long n;
        cin >> n;
        long long x = n * BASE;
        long long y = n * BASE_PLUS_1;
        cout << x << " " << y << "\n";
    }
    return 0;
}

H

我们要计算数组所有非空子串的权值之和,这里的权值定义为:从左到右遍历子串,每遇到一个新的数字,就把当前累计的不同数字个数加到总权值里。

直接暴力枚举所有子串会超时,所以我们用贡献法

每个数字 a[i] 对总权值的贡献,等于它作为 “首次出现” 的那个数字,在所有包含它的子串中,对权值的贡献之和。

对于位置 i 上的数字 a[i],设它上一次出现的位置是 last(如果没出现过则为 0),那么:

  • 它能作为 “首次出现” 的子串左端点范围是 [last+1, i],共 i - last 种选择。
  • 子串右端点范围是 [i, n],共 n - i + 1 种选择。
  • 每个这样的子串中,a[i] 会贡献 1 次权值(因为它是首次出现的新数字)。

因此,a[i] 的总贡献为:(i - last) * (n - i + 1)。

把所有位置的贡献相加,就是最终答案。

代码展示

#include<bits/stdc++.h>
using namespace std;
int main(){
    int T = 1;
    cin >> T;
    while(T--){
        int n;
        cin >> n;
        map<int, vector<int>> mp;
        for(int i = 1; i <= n; i++){
            int x;
            cin >> x;
            mp[x].push_back(i);
        }
        long long ans = 0ll;
        for(pair<const int, vector<int>> &p : mp){
            vector<int> &y = p.second;
            y.insert(y.begin(), 0);
            for(int i = 1; i < y.size(); i++){
                int l = y[i - 1];
                int r = n - y[i] + 1;
                ans += 1ll * r * (r + 1) / 2 * (y[i] - l);
            }
        }
        cout << ans << endl;
    }
    return 0; 
}

E

行和与列和的排列:我们可以让第 i 行的和为 i-1,这样行和自然就是 0 ~ n-1 的排列。

  • 构造矩阵:我们可以构造一个矩阵,使得第 i 行的前 i-1 个元素为 1,后面的为 0。这样行和就是 i-1。
  • 调整列和:为了让列和也成为 0 ~ n-1 的排列,我们可以对矩阵进行转置或其他变换。
  • 控制连通块:通过构造 “对角线” 或 “阶梯” 状的 0 和 1 分布,可以精确控制连通块的数量

代码展示

#include<bits/stdc++.h>
using namespace std;
int main(){
   int n;
	cin>>n;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++)
			cout<<(min(i,j)&1);
		cout <<endl;
	}
  return 0;
}
全部评论

相关推荐

axiom15:校友,我感觉你这个简历去华子暑期实习随便去了
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务