本文合作者:laybxc
赛事信息
名称 | 出题人 | 开始时间 | 时长 | 官方题解 |
---|---|---|---|---|
Codeforces Round #705 (Div. 2) | 74TrAkToR AlFlen | Mar/06/2021 22:05 (UTC+8) | 02:15 | Tutorial |
A. Anti-knapsack
题目
题目描述
给定 ,请选择 的若干个整数,使得这些整数的和的子集都不等于 ,问最多能选出几个整数且分别是多少。
输入格式
第一行是一个整数 ,表示数据组数。
每组数据有两个整数
输出格式
对于每组数据
第一行输出一个整数 ,表示最多能选出的整数数量。
第二行输出这些选出的整数,顺序任意。
解决方案:
思路:
显然对 , 时 这个数肯定可以直接选
显然不行
考虑 的情况:我们发现 选 不选 ,选 不选
如何证明其正确性?
如果选了大于 的数,一定会有两个加起来等于 ,所以最多选 个,那只需要选大的就可以了
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1010;
int t, k, n;
int a[N], ans[N];
int cnt;
int main()
{
ios::sync_with_stdio(0);
cin >> t;
while (t--)
{
cin >> n >> k;
cnt = 0;
memset(a, 0, sizeof(a));
memset(ans, 0, sizeof(ans)); //初始化
if (n > k)
{
for (int i = k + 1; i <= n; i++)
{
ans[++cnt] = i;
}
}
for (int i = k - 1; i >= 1; i--)
{
if (!a[i])
{
ans[++cnt] = i;
a[k - i] = 1;
}
}
cout << cnt << endl;
for (int i = 1; i <= cnt; i++)
{
cout << ans[i] << " ";
}
cout << endl;
}
return 0;
}
B. Planet Lapituletti
题目
题目描述
某行星上的时间一天有 小时,一小时有 分钟,它的时钟也是HH:MM
的形式存在。
当某个点在镜子中显现的时间也是合法的时候,我们认为这个点是合法的。
给定一个时间点,求最近的合法点(如果它本来就是一个合法的时间点,那么就输出这个时间点)
输入格式
第一行是一个整数 ,表示数据组数。
每组数据有两行:
第一行输入
第二行输入给定的那个时间点
输出格式
对于每组数据
第一行输出一个整数 ,表示最多能选出的整数数量。
以 的形式输出最近的合法时间点
解决方案:
思路
显然,合法的 必要不充分条件 是 数字在屏幕上显示是正确的。即:1 2 5 8 0
可以出现,3 4 6 7 9
不能出现。
其次,必须满足对应的 分钟 和 小时 在该行星上时间范围内的条件。
那么就很容易判断一个时间是否合法,问题来到如何找到下一个合法的时间?
枚举即可!
注意前导零?——改用字符串来处理即可
代码:
(比赛的时候写的太冗长了,这是一个需要改进的地方)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1010;
int t, h, m;
int hh, mm;
char H[2], M[2];
char ansh[2], ansm[2];
int nh, nm;
int no[] = {3, 4, 6, 7, 9};
int solve(int x)
{
int res = 0;
char op[2];
if (x == 1)
op[0] = H[0], op[1] = H[1];
else
op[0] = M[0], op[1] = M[1];
for (int j = 1; j >= 0; j--)
{
int num = op[j] - '0';
res *= 10;
if (num == 0)
res += 0;
else if (num == 1)
res += 1;
else if (num == 2)
res += 5;
else if (num == 5)
res += 2;
else if (num == 8)
res += 8;
}
return res;
}
int check()
{
for (int j = 0; j <= 1; j++)
{
int x = H[j] - '0';
for (int i = 0; i < 5; i++)
if (x == no[i])
return 0;
}
for (int j = 0; j <= 1; j++)
{
int x = M[j] - '0';
for (int i = 0; i < 5; i++)
if (x == no[i])
return 0;
}
nh = solve(0), nm = solve(1);
if (nh < h and nm < m)
{
//cout<<hh<<" "<<mm<<endl;
ansh[0] = hh / 10 + '0', ansh[1] = hh % 10 + '0';
ansm[0] = mm / 10 + '0', ansm[1] = mm % 10 + '0';
return 1;
}
return 0;
}
int main()
{
cin >> t;
while (t--)
{
cin >> h >> m;
scanf("%d:%d", &hh, &mm);
H[0] = hh / 10 + '0', H[1] = hh % 10 + '0';
M[0] = mm / 10 + '0', M[1] = mm % 10 + '0';
while (!check())
{
mm = (M[0] - '0') * 10 + M[1] - '0';
hh = (H[0] - '0') * 10 + H[1] - '0';
mm++;
if (mm >= m)
{
mm = 0, hh++;
}
if (hh >= h)
hh = 0;
H[0] = hh / 10 + '0', H[1] = hh % 10 + '0';
M[0] = mm / 10 + '0', M[1] = mm % 10 + '0';
}
cout << ansh[0] << ansh[1] << ":" << ansm[0] << ansm[1] << endl;
}
return 0;
}
C. K-beautiful Strings
题目
题目描述
一个字符串是 美的定义:字符串中不同字符出现的总次数能整除
现在给你一个 ,和一个字符串, 为字符串的长度,让你求一个字符串满足 美的定义并且这个字符串的字典序要尽可能的小(前提是大于等于原字符串)
并且长度和原串长度相同,如果答案字符串不存在,输出-1
.
输入格式
第一行是一个整数 ,表示数据组数。
每组数据有两行:
第一行输入两个整数 ,分别为字符串 的长度和给定的数字
第二行输入只含小写字母的字符串
输出格式
对于每一组测试数据,输出第一个字典序大于等于 且满足 美定义的字符串
解决方案
思路:
因为要求的字符串要尽可能的小,还必须大于等于原串,那么我们可能的最优解就是原串本身满足k美,直接输出原串。
如果一个串满足 美,那么说明串的长度是由一组 的倍数组成的,所以如果 不能整除 ,那么找不到满足 美的字符串,所以我们直接输出 。
注意是 的倍数,而不是每个出现次数都是 ,因此类似 这样的数据都是合法的。
如何寻找满足 美的最小字符串?我们可以这样 贪心 的想,构造一个最小的大于等于原串的k美字符串,那么我们应该对原串从后往前构造,因为越往前,构造出来的字符串就越大,所以尽可能在最后就构造好,这样满足字典序最小的性质。
我们首先统计每个字符出现的次数,用一个
cnt
数组存储,然后统计将所有字符都变成k的倍数时所需增加的字符总数tot
:即字符 变成 的倍数时需要增加的字符数为 ;从后往前遍历,遍历到位置 时,就判断一下从位置i开始之前的子串是否是一个不用修改的最长前缀子串(因为前面的串不改最优)
然后得到位置 上的字符 ,并逐个判断要放哪个字符
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int cnt[30];
int n, t, k;
char s[100020];
void solve()
{
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; i++)
{
cnt[s[i] - 'a']++; //对原串中出现的字母进行统计
}
int tot = 0;
for (int i = 0; i <= 25; i++)
{
tot += (k - cnt[i] % k) % k; //表示将字符i变成 k的倍数 时所需要增加的字母数量
}
if (tot == 0)
{
for (int i = 1; i <= n; i++)
cout << s[i];
cout << endl;
}
else if (n % k != 0)
cout << "-1" << endl;
else
{
int ans = 0;
for (int i = n; i >= 1 and ans == 0; i--) //从后往前遍历
{
tot -= (k - cnt[s[i] - 'a'] % k) % k;
cnt[s[i] - 'a']--;
tot += (k - cnt[s[i] - 'a'] % k) % k; //统计i位置之前的tot
for (int j = s[i] - 'a' + 1; j <= 25; j++) //修改i
{
int last = tot;
tot -= (k - cnt[j] % k) % k;
cnt[j]++;
tot += (k - cnt[j] % k) % k;
if (i + tot <= n) //如果需要增加(修改)的字符数不超过i及其后面这一段的长度,那么就说明我们找到了最长的不用修改的前缀子串
{
for (int x = 1; x < i; x++)
{
cout << s[x];
}
cout << char(j + 'a'); //输出i位置的字符
//因为i位被改大了,所以到现在,字典序已经变大了,后续的直接放最小的即可
for (int x = 1; x <= n - i - tot; x++)
{
cout << 'a'; //补a,因为i之前的字符经过补位后的修改后一定是k倍,所以剩下的位数一定是k倍,那么直接都补上a即可
}
for (int x = 0; x <= 25; x++) //从前往后,补充缺少的字符(补位)
{
int num = (k - cnt[x] % k) % k;
while (num > 0)
{
tot--, num--;
cout << char(x + 'a');
}
}
cout << endl;
ans = 1;
break;
}
tot = last;
cnt[j]--;
}
}
}
}
int main()
{
cin >> t;
while (t--)
{
cin >> n >> k;
cin >> (s + 1);
solve();
}
return 0;
}
D. GCD of an Array
题目
题目描述
一个长度为 的数组 。处理以下格式的 个查询:给定整数 和 ,将 乘以 。
在处理每个查询之后,您需要输出数组 的所有元素的最大公约数(GCD)。
因为答案可能太大,所以要求以 的模输出它。
输入格式
第一行是两个整数
第二行包括 个整数
接下来 行包括 个查询,每行包括两个数字
输出格式
输出 行,每行为现在数组的 GCD 对 的模数。
解决方案
数论+数据结构题先不写(