Codeforces Round 925 (Div. 3) A-G题解
A. Recovering a Small String Problem - A - Codeforces
题目概述:根据所给的 n n n输出相应的词性最小的3个小写拉丁字母。
其中 n n n的含义为三个字母的编号相加总和(a-z的编号分别对应着1-26)。
词性最小(lexicographically smallest)的含义是字典序最小。
题目类型:贪心,构造。
解题思路:
根据题意贪心构造即可,下面提供两种思路。
AC代码:
贪心分支模拟
//
// Created by Mrlaolu on 2024/2/13.
//
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
void solve() {
// code here
int n;
cin >> n;
vector<char>ans(3);
if(n >= 28)
{
ans[2] = 'z';
n -= 26;
if(n >= 27)
{
ans[1] = 'z';
n -= 26;
ans[0] = n-1 + 'a';
}
else
{
ans[1] = n - 2 + 'a';
ans[0] = 'a';
}
}
else
{
ans[2] = n - 3 + 'a';
ans[1] = 'a';
ans[0] = 'a';
}
for(int i = 0;i < 3;++i)
{
cout << ans[i];
}
cout << endl;
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
还有一种通过三重for循环暴力找词性最小的办法,更为简洁
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
void solve() {
// code here
int n;
cin >> n;
for (int i = 1; i <= 26; i++) {
for (int j = 1; j <= 26; j++) {
for (int k = 1; k <= 26; k++) {
if (i + j + k == n) {
cout << char(i - 1 + 'a') << char(j - 1 + 'a') << char(k - 1 + 'a') << endl;
return;
}
}
}
}
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
B. Make Equal Problem - B - Codeforces
题目概述:给你 n n n 个正整数数字 a 1 − a n a_1-a_n a1−an,它们的和保证能被 n n n 整除。你进行任意次(包括 0 0 0 次)把 a i a_i ai 的数值分点给 a j a_j aj 的操作,其中 i i i 必须小于 j j j(即 i < j i < j i<j )且每次操作 i i i 和 j j j 都可变。问你能不能使该数组全变为平均数。
题目类型:贪心,模拟
解题思路:
先算出这 n n n 个数字的平均数,记作 a v e ave ave。因为可以进行无数次题目种所提到的操作,只要 i < j i < j i<j,所以我们创造一个变量 s p a r e spare spare,从后往前遍历数组 a i a_i ai。只要 a i > a v e a_i > ave ai>ave,我们就把多余的数值放到 s p a r e spare spare 里,如果 a i < a v e a_i < ave ai<ave ,则把 s p a r e spare spare 里的值取出来给 a i a_i ai。如果某时刻 s p a r e < 0 spare < 0 spare<0,则说明排在后面的数字多余的值不足以填上前面的数,输出“NO”;反正如果到结束都 s p a r e ≥ 0 spare \ge 0 spare≥0,则输出“YES”;
AC代码:
//
// Created by Mrlaolu on 2024/2/13.
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
void solve() {
// code here
int n;
cin >> n;
vector<int>arr(n);
int sum = 0;
for(int i = 0;i < n;++i)
{
cin >> arr[i];
sum += arr[i];
}
int ave = sum / n;
int spare = 0;
for(int i = 0;i < n;++i)
{
if(spare < ave - arr[i])
{cout << "NO\n";return;}
else
{
spare -= ave - arr[i]; //直接利用负负得正原理,少掉对ave-arr[i]正负的判断
}
}
cout << "YES\n";
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
C. Make Equal Again Problem - C - Codeforces
题目类型:贪心
解题思路:
- 因为只有一次操作机会,所以我们要变也是要把位于中间的数全变成相同的数值。
- 根据贪心的思想,我们要获得最小布尔值(burles),就要 j − i j-i j−i 尽可能小,也就是尽可能少的两端的数包含进去。所以我们就去找从两端( a 0 a_0 a0 和 a n − 1 a_{n-1} an−1 )是不是相等。如果相等,从这两个的开始的连续相同子串为多长,答案就为 n − (从 a 0 开始的连续相同子串 + 从 a n − 1 开始的连续相同子串) n-(从a_0开始的连续相同子串+从a_{n-1}开始的连续相同子串) n−(从a0开始的连续相同子串+从an−1开始的连续相同子串)。如果不相等,则为 n − M A X (从 a 0 开始的连续相同子串 , 从 a n − 1 开始的连续相同子串) n-MAX(从a_0开始的连续相同子串,从a_{n-1}开始的连续相同子串) n−MAX(从a0开始的连续相同子串,从an−1开始的连续相同子串)
AC代码:
//
// Created by Mrlaolu on 2024/2/13.
//
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
using namespace std;
void solve() {
// code here
int n;
cin >> n;
vector<int>arr(n);
int sum = 0;
for(int i = 0;i < n;++i)
{
cin >> arr[i];
}
int fsum = 1;
for(int i = 1;i < n;++i)
{
if(arr[i] == arr[0])
{fsum += 1;}
else
{break;}
}
int bsum = 1;
for(int i = n - 2;i >= 0;--i)
{
if(arr[i] == arr[n - 1])
{bsum += 1;}
else
{break;}
}
if(arr[n - 1] == arr[0])
{cout << MAX(n-bsum-fsum,0) << endl;} //因为bsum+fsum >= n可以说明整个数组的数都相同 所以直接这样写输出0
else
{cout << n-MAX(bsum,fsum) << endl;}
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D. Divisible Pairs [Problem - D - Codeforces]
题目类型:预处理,模的性质
解题思路:
- 根据模的性质 ( a ± b ) m o d p = ( a m o d p ± b m o d p ) m o d p (a \pm b) \bmod p = (a \bmod p \pm b \bmod p) \bmod p (a±b)modp=(amodp±bmodp)modp 可以发现,只要两个数的余数相加能被 x x x 整除,相减能被 y y y 整除,就可满足题意。
- 我们可以先把每个数字预处理,分别对 x x x 和 y y y 进行取模并存储到 map 里,匹配时直接在 map 里找{ ( x − a i m o d x ) m o d x , a i m o d y (x - a_i \bmod x) \bmod x,a_i \bmod y (x−aimodx)modx,aimody}所对应的相应的个数即可。(需要前置知识 pair 和 map )
- 需要特别注意的时,可能存在 ( x − a i m o d x ) m o d x = a i m o d x (x - a_i \bmod x) \bmod x = a_i \bmod x (x−aimodx)modx=aimodx,而匹配时不能与自身匹配,所以要特判这种情况,如果出现则对答案 − 1 -1 −1 ,因为 i < j i < j i<j ,所以要对最终答案 ÷ \div ÷ 2 2 2 。
AC代码:
//
// Created by Mrlaolu on 2024/2/13.
//
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
using namespace std;
void solve() {
// code here
int n,x,y;
cin >> n >> x >> y;
vector<int>arr(n);
map<pair<int,int>,vector<int>>cate; //用于分类存储每个数与x和y取余后的数
for(int i = 0;i < n;++i)
{
cin >> arr[i];
cate[{arr[i] % x,arr[i] % y}].push_back(arr[i]);
}
int ans = 0;
for(int i = 0;i < n;++i)
{
if(cate.count({(x - arr[i] % x) % x,arr[i] % y}))ans += cate[{(x - arr[i] % x) % x,arr[i] % y}].size();
if((x - arr[i] % x) % x == arr[i] % x) //(3)所提到的特殊情况
{ans -= 1;}
}
cout << ans / 2 << endl;
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
E. Anna and the Valentine’s Day Gift Problem - E - Codeforces
题目类型:博弈
解题思路:
- Anna 要使最终最后一个数的位数最小,Sasha 要使最终最后一个数的位数最大 。
- 对 Anna 最有利的操作是把后缀零最多的数给倒置(如 158000 158000 158000 和 1580 1580 1580 都能倒置成 851 851 851 ,所以我们优先选 158000 158000 158000 可以使最后的位数少 3 3 3 位)。
- 对 Sasha 最有利的操作是把后缀零最多的数后面连接一个数,Anna 就不能通过倒置使这些后缀零(如 158000 158000 158000 和 1580 1580 1580 和 851 851 851 ,我们选择把 158000 158000 158000 与 851 851 851 连接成 158000851 158000851 158000851 为 Sasha 的最有利操作)。
- 综上所述,我们可以把每个数的位数和的总和先加起来,并把后缀零个数求出来存到数组里。把数组按降序排序,再根据题目所讲的Anna先手Sasha后手隔一个减去后缀零的个数,即 b i t = b i t − ∑ k = 1 n / 2 a 2 k − 1 bit = bit - \sum_{k=1}^{n / 2}a_{2k-1} bit=bit−∑k=1n/2a2k−1。
AC代码:
//
// Created by Mrlaolu on 2024/2/13.
//
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
void solve() {
// code here
int n,m;
cin >> n >> m;
vector<int>arr(n);
int bit = 0; //存储位数和的总和
vector<int>zero(n); //存储后缀零个数的数组
for(int i = 0;i < n;++i)
{
cin >> arr[i];
string temp = to_string(arr[i]); //通过to_string快速求位数
bit += temp.size();
for(int j = temp.size() - 1;j >= 0;--j) //求后缀0
{
if(temp[j] == '0')
{zero[i]++;}
else
{break;}
}
}
sort(zero.begin(),zero.end(),greater<int>());
for(int i = 0;i < n;i+=2) //隔一个减后缀0的个数
{
bit -= zero[i];
}
if(bit > m){cout << "Sasha\n";}
else
{cout << "Anna\n";}
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
F. Chat Screenshots Problem - F - Codeforces
题目类型:图论
前置知识:拓扑排序
解题思路:
- 这道题最主要的就是判断给你的这几个序列有没有矛盾的地方,我们可以把每一条顺序看作是一个有向图,由前往后一直指下去。比如顺序
1 2 3 4 5
就相当于2→3 3→4 4→5
。我们把这个几个序列中产生的有向边放到一个有向图里。如果产生了环,则说明有矛盾产生,不能生成截图;否则就没有。而我们可以通过拓扑排序的性质判断有没有环(即如果产生了环,必有元素最终的入读不为 0 0 0 ,没有进入队列) - 还有个思路是jls在群里提出的,即如果两个顺序的提供者位置不是相邻的(如果是相邻的则要三个),则这个截图就可以确定下来了(或者产生不了截图),后来的顺序只要和这个顺序一样就能产生。(这里只是提供一个思路,未提供ac代码)
AC代码:
//
// Created by Mrlaolu on 2024/2/13.
//
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
using namespace std;
void solve() {
// code here
int n,k;
cin >> n >> k;
vector<vector<int>>arr(k,vector<int>(n));
vector<vector<int>>a(n + 1); //用于存ta指向的节点
vector<int>in(n + 1); //用于存各个节点的入度
vector<int>ans;
for(int i = 0;i < k;++i)
{
for(int j = 0;j < n;++j)
{
cin >> arr[i][j];
}
}
for(int i = 0;i < k;++i)
{
for(int j = 1;j < n - 1;++j)
{
a[arr[i][j]].push_back(arr[i][j + 1]);
in[arr[i][j + 1]]++;
}
}
queue<int>que; //用队列和栈都一样
for(int i = 1;i <= n;++i)
{
if(!in[i]){que.push(i);ans.push_back(i);} //预处理 将入读为0的都放进队列
}
while(que.size())
{
int temp = que.front();
que.pop();
for(int i = 0;i < a[temp].size();++i)
{
in[a[temp][i]]--;
if(!in[a[temp][i]]){que.push(a[temp][i]);ans.push_back(a[temp][i]);}
}
}
for(int i = 1;i <= n;++i)
{
if(in[i] != 0){cout << "NO\n";return;}
}
cout << "YES\n";
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
G. One-Dimensional Puzzle Problem - G - Codeforces
题目类型:组合排列
前置知识:乘法逆元,组合数求法,隔板法
解题思路:
-
我们可以发现 1 1 1 和 2 2 2 是不能自我连接的,而 3 3 3 和 4 4 4 是可以的。这就表明一个 1 1 1 和 2 2 2 是相伴相生的,而 3 3 3 和 4 4 4 可以看作是插在 1 1 1 和 2 2 2 中间 或者 1 1 1 或 2 2 2 两侧的可有可无的东西。
-
根据上面这条分析和样例,我们可以发现 1 1 1 和 2 2 2 的数量差不能大于 1 1 1 ,不然就有匹配失败而被孤立的 1 1 1 或 2 2 2 ,使其不能拼成一条链。而 3 3 3 和 4 4 4 只用看作插在 1 1 1 和 2 2 2 中间或两侧就可以了。根据 1 1 1 和 2 2 2 之间可以没有插 3 3 3 和 4 4 4 (分组可空)的性质,我们可以想到用隔板法做这道题(不会隔板法的可以去搜视频学一下)。
-
如果 1 1 1 的数量比 2 2 2 多,则以 1 1 1 为隔板插,否则以 2 2 2 为隔板插。因为数量多就说明侧边的那两个拼图是它,所以我们以它为隔板插。
-
当 1 1 1 和 2 2 2 的数量相同时,我们注意到这条链的主链有两条拼接方法,一个为 1 1 1 在最左侧,一个为 1 1 1 在最右侧,我们便要把这两个情况加起来。(没有理解到的可以自己画图试一试,就能有所体会了)
-
因为涉及到组合数取模的问题,所以我们要用到乘法逆元。(不会的话也要去搜视频学一下)
AC代码:
//
// Created by Mrlaolu on 2024/2/14.
//
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
using namespace std;
const int mod = 998244353;
const int N = 2000005;
int fac[N], jv[N], inv[N];
void com_init(int n) {
jv[0] = fac[0] = 1;
for (int i = 1; i <= n; i++) {
inv[i] = i == 1 ? 1 : (mod - mod / i) * inv[mod % i] % mod;
fac[i] = fac[i - 1] * i % mod;
jv[i] = jv[i - 1] * inv[i] % mod;
}
}
int com(int n, int m) {
if (n < m || m < 0) return 0;
return fac[n] * jv[n - m] % mod * jv[m] % mod;
}
void solve() {
// code here
int a,b,c,d;
cin >> a >> b >> c >> d;
if(a == 0 && b == 0 && c == 0 && d == 0)
{
cout << 1 << endl;
return;
}
if(abs(a - b) > 1)
{
cout << 0 << endl;return;
}
else if(a == b && a == 0)
{
cout << (c == 0 || d == 0) << endl;
}
else
{
if(a > b)
{
cout << com(c + a - 1,a - 1) * com(d + a - 1,a - 1) % mod << endl;
}
else if(a < b)
{
cout << com(c + b - 1,b - 1) * com(d + b - 1,b - 1) % mod << endl;
}
else
{
cout << (com(c + b,b) * com(d + b - 1,b - 1) % mod + com(c + b - 1,b - 1) * com(d + b,b) % mod) % mod << endl;
}
}
}
signed main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
com_init(2e6);
while (t--) {
solve();
}
return 0;
}