A. We Got Everything Covered! Problem - A - Codeforces
题目概述:给你正整数 n n n 和 k k k , 要你构造出一个字符串 s s s ,使得所有可能的长度为 n n n 的由前 k k k 个字母组成的字符串 都是字符串 s s s 的子序列 。
注:如果从 b b b 中删除一些字符(可能为零)而不改变其余字符的顺序,就可以得到 a a a ,那么字符串 a a a 就被称为另一个字符串 b b b 的子序列(子串)。
题目类型:构造
解题思路:
1.首先要注意到样例三的答案可以是 a b a b abab abab,样例四的答案可以是 a b c a b c abcabc abcabc .再次观察样例,我们可以发现–似乎 n n n 是由前 k k k 个字母组成的字符串循环出现的次数.(到这里就可以把题目做出来了)
2.上述要点1的成功的原因,其实是因为只要有前 k k k 个字母在某个区域都出现一次,这样的区域有 n n n 个(这 n n n 个区域不重叠),那么就可以构造出 所有可能的长度为 n n n 的由前 k k k 个字母组成的字符串.(这是这种子串题的重要结论).
3.对要点2进行论证也很简单:把构造长度为 n n n 字符串看作往 n n n 个格子里填字母,这些格子对应各自着一个区域,只要这些区域里这 k k k 个字母都能取到,当然就能构造出这个字符串.
AC代码:
//
// Created by Mrlaolu on 2024/1/27.
//
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
void solve() {
// code here
int n,k;
cin >> n >> k;
int cnt = 0;
for(int i = 0;i < n;++i)
{
for(int p = 0;p < k;++p)
{
char a = p + 97;
cout << a ;
cnt++;
}
}
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;
}
B. A Balanced Problemset? Problem - B - Codeforces
题目概述:给你正整数 x x x 和 n n n ,你需要把这个 x x x 分为 n n n 份,且这 n n n 份的 G C D GCD GCD(最大公约数) 最大,输出这个 G C D GCD GCD。
题目类型:贪心,枚举。
解题思路:
1.首先要注意到这个题目数据范围很大 , O ( n ) O(n) O(n)基本上过不了(本人就因此被hack了(悲)),所以要寻找 O ( l o g n ) O(logn) O(logn) 的写法。
2.观察题目,由GCD的特性注意到一个式子 − − -- −− x = G C D ∗ x 1 + G C D ∗ x 2 + . . . + G C D ∗ x n = G C D ∗ ( x 1 + x 2 + . . . + x n ) = G C D ∗ s u m x = GCD * x_1 + GCD * x_2 + ... + GCD * x_n = GCD * (x_1 + x_2 + ... + x_n) = GCD * sum x=GCD∗x1+GCD∗x2+...+GCD∗xn=GCD∗(x1+x2+...+xn)=GCD∗sum
再观察式子可以发现 s u m ≥ n sum \ge n sum≥n (因为 s u m = x 1 + . . . + x n ( x i ≥ 1 ) sum = x_1+...+x_n(x_i \ge 1) sum=x1+...+xn(xi≥1) ) 和 G C D = x / s u m GCD = x / sum GCD=x/sum ( O ( l o g n ) O(logn) O(logn)的关键)
所以我们枚举 i i i ( x x x 的因数)时可以同时把 i i i 看作 s u m sum sum 和 G C D GCD GCD 。只要 s u m ≥ n sum \ge n sum≥n 成立这个数( i i i 或 x / i x / i x/i )就可以作为最终答案的候选
AC代码:
//
// Created by Mrlaolu on 2024/1/27.
//
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
void solve()
{
int n,x;
cin >> x >> n;
int ans = 0;
for(int i = 1;i * i <= x;++i)
{
if(x % i == 0)
{
if(i >= n){ans = max(ans,x / i);}
if(x / i >= n){ans = max(ans,i);}
}
}
cout << ans << 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;
}
C. Did We Get Everything Covered? Problem - C - Codeforces
**题目概述:**给你正整数 n n n 和 k k k,字符串 s s s,你需要验证 所有 长度为 n n n 的由前 k k k 个字母组成的字符串 都是字符串 s s s 的子序列,如果是输出"YES",否则输出"NO"并给出符合题意但不为字符串 s s s 的子序列的字符串。
注:如果从 b b b 中删除一些字符(可能为零)而不改变其余的顺序,就可以得到 a a a,那么字符串 a a a 就被称为另一个字符串 b b b 的子序列(子串)。
题目类型:构造
解题思路:
1.首先要知道一个重要结论 − − -- −−只要有前 k k k 个字母在某个区域都出现一次,这样的区域有 n n n 个(这 n n n 个区域不重叠),那么就可以构造出 所有可能的长度为 n n n 的由前 k k k 个字母组成的字符串。
2.对要点1进行论证也很简单:把构造长度为 n n n 字符串看作往 n n n 个格子里填字母,这些格子对应各自着一个区域,只要这些区域里这 k k k 个字母都能取到,当然就能构造出这个字符串。否则就能找出特例。
3.解决了验证问题,下面就是如何构造特例了。注意到上述所提的"区域"的最后一个字母一定在这个"区域"中只出现过一次(因为我们贪心划分时,就是所有字母都出现过后,马上划为的一个独立的区域)。所以我们只用把这些已经划好的区域中的最后一个字母取出来,再结合后面那些不完整的区域(因为的时候验证为"NO"的时候是"完整区域"的个数< n,肯定存在不完整的区域)中没有出现字母,组成一个字符串,便是特例了。(如果有点懵可以结合要点2再理解一下)
AC代码:
//
// Created by Mrlaolu on 2024/1/27.
//
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int arr[1100][30][30];
int fro[1100][30];
void solve() {
// code here
int n,k,m;
string s;
cin >> n >> k >> m;
cin >> s;
vector<int>last(k + 1);
int cnt = 0;
int cycle = 0;
string ans;
vector<bool>check(k,true);
for(int i = 0;i < s.size();++i)
{
if(check[s[i] - 'a']){cycle++;check[s[i] - 'a'] = false;}
if(cycle == k){cnt++;cycle = 0;ans.push_back(s[i]);fill(check.begin(),check.end(),true);}
}
if(cnt >= n){cout << "YES\n";return;}
cout << "NO\n";
cout << ans;
for(int p = 0;p < n - ans.size();++p)
{
for(int i = 0;i < k;++i)
if(check[i]){cout << (char)(i + 'a') ;break;}
}
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;
}