AtCoder Beginner Contest 350

补题链接 未补(F~G)

 

A - Past ABCs

题目描述

您将获得一个长度为 \(6\) 的字符串 \(S\) 。保证 \(S\) 的前三个字符是“ABC”,后三个字符是数字。

确定 \(S\) 是否为本次竞赛开始前在 AtCoder 上举行并结束的竞赛的缩写。

此处,字符串 \(T\) 是“本次比赛开始前在 AtCoder 上举行并结束的比赛的缩写”当且仅当它等于以下 \(348\) 字符串之一:

ABC001ABC002\(\ldots\)ABC314ABC315ABC317ABC318\(\ldots\)ABC348ABC349

请注意,不包括“ABC316”。

分析

已经保证了前三个字符的合法性,那么只需要判断后三个是否合法
又因为保证了后三个是数字,那么把后三个转换成\(int\)更容易判断合法性

代码

#include <bits/stdc++.h>

#define int long long
#define all(x) (x).begin(), (x).end()
#define len(x) (x).size()
#define endl '\n'
#define lowbit(x) ((x) & - (x))

//using namespace std;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;


signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    //std::cout.precision(10);
    std::string s;
    std::cin >> s;
    //substr(idx, len)从字符串下标为idx的位置开始,截取长度为len的字符串
    //stoi(str)将字符串str转换成整数
    int x = std::stoi(s.substr(3, 3));
    if(x >= 1 && x <= 349 && x != 316)
    	std::cout << "Yes";
    else
    	std::cout << "No";

    return 0;
}

 

B - Dentist Aoki

题目描述

高桥有 \(N\) 颗牙齿,每个编号为 \(1, 2, \dots, N\) 的孔中都有一颗。
牙医青木将对这些牙齿和洞进行 \(Q\) 治疗。
在第 \(i\) 次处理中,孔 \(Ti\) 的处理如下:

  • 如果孔 \(T i\) 中有齿,请将齿从孔 \(T i\) 中取出。
  • 如果孔 \(T i\) 中没有齿(即孔是空的),则在孔 \(T i\) 中长出齿。

所有治疗完成后,高桥有多少颗牙齿?

分析

\(0\)表示没牙齿,\(1\)表示有牙齿
对于每次操作当前位置的值为\(0\)就改为\(1\),当前位置的值为\(1\)就改为\(0\)
那么这个操作可以用异或(相同为0,不同为1)来实现,异或1
1 ^ 1 相同 ---> 0
0 ^ 1 不同 ---> 1

代码

#include <bits/stdc++.h>

#define int long long
#define all(x) (x).begin(), (x).end()
#define len(x) (x).size()
#define endl '\n'
#define lowbit(x) ((x) & - (x))

//using namespace std;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;


signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    //std::cout.precision(10);
    int n, q;
    std::cin >> n >> q;
    std::vector<int> f(n + 1, 1);//一开始每个孔都有牙齿
    while(q--) {
    	int x;
    	std::cin >> x;
    	f[x] ^= 1;
    }
    int cnt = 0;
    for(int i = 1; i <= n; i++) {
    	if(f[i])
    		cnt++;
    }
    std::cout << cnt;
    return 0;
}

 

C - Sort

题目描述

您将获得 \((1,2,\ldots,N)\) 的排列 \(A=(A 1,\ldots,A N)\)
通过在 \(0\)\(N-1\) 次(含)之间执行以下操作,将 \(A\) 转换为 \((1,2,\ldots,N)\)

  • 操作:选择任意一对整数 \((i,j)\) ,使得 \(1\leq i j \leq N\) 。交换 \(A\) 的第 \(i\) 个位置和第 \(j\) 个位置的元素。

可以证明,在给定的约束条件下,总是可以将 \(A\) 变换为 \((1,2,\ldots,N)\)

分析

给定\(1\)\(n\)的排列\(A\),让我们通过一定操作使得\(Ai = i\)
因此我们的目标是通过交换位置这一手段将每个数放在合法位置上,我们只需要每次把位置不合法的数放在合法位置即可
证明最大为\(n-1\)次:
假设所有数都不在合法位置,我们可以通过\(n - 1\)次操作把前\(n - 1\)个数放在合法位置,那么第\(n\)个数自然而然在合法位置

代码

#include <bits/stdc++.h>

#define int long long
#define all(x) (x).begin(), (x).end()
#define len(x) (x).size()
#define endl '\n'
#define lowbit(x) ((x) & - (x))

//using namespace std;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;


signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    //std::cout.precision(10);
    std::map<int, int> find;
    int n;
    std::cin >> n;
    std::vector<int> a(n + 1), have;
    for(int i = 1; i <= n; i++) {
    	std::cin >> a[i];
    	if(a[i] != i) {
    		find[a[i]] = i;
            //收集不合法的数
    		have.push_back(a[i]);
    	}
    }
    //从大到小排序
    std::sort(all(have), std::greater<int>());
    std::vector<std::pair<int, int>> ans;
    while(len(have)) {
        //每次取出为合法数中最小的数
    	int now = have.back();
        //查找这个数的位置
    	int idx = find[now];
    	have.pop_back();
    	if(now == idx) {
            //已经在之前的交换过程中变得合法了
    		continue;
    	}
        //把值为now的数放在now位置上,把now位置上的数放在idx位置上
        ans.push_back({now, idx});
    	find[a[now]] = idx;
    	find[a[idx]] = now;
    	std::swap(a[now], a[idx]);

    }
    if(len(ans)) {
        std::cout << len(ans) << endl;
        for(int i = 0; i < len(ans); i++) {
            std::cout << ans[i].first << ' ' << ans[i].second << endl;
        }
    } else {
        std::cout << 0;
    }

    return 0;
}

 

D - New Friends

问题描述

有一个由 \(N\) 用户使用的 SNS,标记为从 \(1\)\(N\) 的数字。

在这个SNS中,两个用户可以互相成为朋友
友谊是双向的;如果用户 X 是用户 Y 的朋友,则用户 Y 始终是用户 X 的朋友。

目前,SNS 上有 \(M\) 对好友,其中第 \(i\) 对由用户 \(A i\)\(B i\) 组成。

确定以下操作可以执行的最大次数:

  • 操作:选择三个用户X、Y、Z,其中X和Y是朋友,Y和Z是朋友,但X和Z不是。使 X 和 Z 成为朋友。

分析

一个简单图(图中没有自环和重边)那么它的最大边数为对于所有节点,两两节点间都有一条边
也就是 \(C(2, n)=n!/[2!\times(n - 2)!]=n\times(n-1)/2\) ,这就是\(n\)个节点的简单图的最大边数
我们可以利用这个性质,对于题目构建的图求连通块中的点数\(pointCnt\)和边数\(pathCnt\),该联通块提供的贡献就是 \(C(2, pointCnt)-pathCnt\)

代码

#include <bits/stdc++.h>

#define int long long
#define all(x) (x).begin(), (x).end()
#define len(x) (x).size()
#define endl '\n'
#define lowbit(x) ((x) & - (x))

//using namespace std;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;


signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    //std::cout.precision(10);
    int n, m;
    std::cin >> n >> m;
    std::vector<std::vector<int>> path(n + 1);
    for(int i = 1; i <= m; i++) {
    	int u, v;
    	std::cin >> u >> v;
    	path[u].push_back(v);
    	path[v].push_back(u);
    }
    std::vector<int> f(n + 1);
    int ans = 0;
    std::map<std::pair<int,int>, int> flag;
    auto dfs = [&](auto self, int now, int fa, int& path_cnt) -> int {
        
        int point = 1;
        f[now] = 1;
        
    	for(auto it : path[now]) {
            if(!flag[{now, it}]) {
                path_cnt++;
                flag[{now, it}] = 1;
                flag[{it, now}] = 1;
            }
    		if(it == fa || f[it])
    			continue;
            point += self(self, it, now, path_cnt);
    	}
        
    	return point;
    };
    for(int i = 1; i <= n; i++) {
        if(!f[i]) {
            int path_cnt = 0;
            int point_cnt = dfs(dfs, i, -1, path_cnt);
            if(point_cnt & 1) {
                ans += (point_cnt - 1) / 2ll * point_cnt - path_cnt;
            } else {
                ans += point_cnt * (point_cnt - 1) / 2ll  - path_cnt;
            }
            
        }
    }
    std::cout << ans;
    return 0;
}
posted @ 2024-04-22 22:16  独陷泥沼  阅读(71)  评论(0)    收藏  举报