Codeforces Round #705 (Div. 2)
Contest Info
Solved | A | B | C | D | E | F |
---|---|---|---|---|---|---|
4 / 6 | O | O | O | Ø | - | - |
- O 在比赛中通过
- Ø 赛后通过
- ! 尝试了但是失败了
- - 没有尝试
Solutions
A. Anti-knapsack
题意:
给你两个正整数\(n\)和\(k\),在\(1\)~\(n\)中求一个最大的集合,使得这个集合的任意子集内所有元素的和都不等于\(k\)。
思路:
大于\(k\)的数可以直接取。然后我们考虑\(1\)~\(k - 1\),发现两端对应的数加起来等于\(k\),所以只取一端的数就可以了。
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n, k;
void solve() {
cin >> n >> k;
cout << n - k + k / 2 << endl;
for (int i = k + 1; i <= n; ++i) {
cout << i << " ";
}
for (int i = k - 1; i >= (k + 1) / 2; --i) {
cout << i << " ";
}
cout << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T = 1;
cin >> _T;
while (_T--) solve();
return 0;
}
B. Planet Lapituletti
题意:
我们定义一天有\(h\)个小时,每小时\(m\)分钟。现在给你一个时间,求在未来最接近它的一个时间,使得我们从镜子中读到的时间也是合法的。
思路:
从镜子中读取的时间和数字都刚好是相反的,所以我们搞个数组,先判断下从镜子中能读到的数字是否合法,然后在把读到的时间算出来,和\(h\)还有\(m\)进行比较,判断是否合法。一直往后取直到合法为止。
#include <bits/stdc++.h>
using namespace std;
template <class T> inline void read(T &x) {
int f = 0; x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if (f) x = -x;
}
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int a[] = {0, 1, 5, -1, -1, 2, -1, -1, 8, -1};
int hh, mm, h, m;
int H[3], M[3];
bool check() {
H[1] = a[mm % 10];
H[2] = a[mm / 10 % 10];
M[1] = a[hh % 10];
M[2] = a[hh / 10 % 10];
for (int i = 1; i <= 2; ++i) {
if (H[i] == -1 || M[i] == -1) return false;
}
int fh = H[1] * 10 + H[2];
int fm = M[1] * 10 + M[2];
return fh < h && fm < m;
}
void solve() {
scanf("%d %d", &h, &m);
scanf("%d:%d", &hh, &mm);
while (!check()) {
mm += 1;
if (mm >= m) mm = 0, hh++;
if (hh >= h) hh = 0;
}
printf("%02d:%02d\n", hh, mm);
}
int main() {
int _T = 1;
read(_T);
while (_T--) solve();
return 0;
}
C. K-beautiful Strings
题意:
给你一个长为\(n\),仅由小写字母构成的字符串,求一个字典序大于等于原字符串且字典序最小的字符串,使得其中所有元素的数量都是\(k\)的倍数。
思路:
当\(n\)是\(k\)的倍数时,就一定有解,否则一定无解。
当我们将第i位的字符变大的时候,第\(i + 1\)到第\(n\)位的字符就可以任意改变了。要让字符串的字典序最小,我们应该贪心地让后面的字符变大。所以我们从后往前枚举字符,进行变大处理,同时检查在拥有\(n - i\)个任意字符时,能否让所有字符的数量都变成\(k\)的倍数。如果检查完任意字符仍有剩余,那么由于第一步的特判,剩下来的数量一定也是\(k\)的倍数。我们将剩下来的字符全部处理成'\(a\)',然后先输出前面不是任意字符的字符,然后优先输出剩余的'\(a\)',接下来从'\(a\)'到'\(z\)'输出需要额外添加的字符,即可得到字典序最小的满足要求的字符串。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 7;
char s[N];
int n, k;
int num[207], to[207];
bool check(int go) {
for (int i = 'a'; i <= 'z'; ++i) {
go -= num[i] % k? k - (num[i] % k) : 0;
}
return go >= 0;
}
void solve() {
scanf("%d %d", &n, &k);
scanf("%s", s);
if (n % k) {
puts("-1");
return;
}
for (int i = 'a'; i <= 'z'; ++i) {
num[i] = 0;
}
for (int i = 0; i < n; ++i) {
num[ s[i] ]++;
}
if (check(0)) {
printf("%s\n", s);
return;
}
int len = 0;
for (int i = n - 1; i >= 0; --i) {
int f = 0;
for (int j = s[i] + 1; j <= 'z'; ++j) {
num[j - 1]--;
num[j]++;
if (check(len)) {
f = 1;
s[i] = j;
break;
}
if (j == 'z') {
num[j]--;
num[ s[i] ]++;
}
}
if (f) break;
num[ s[i] ]--; // 注意消除这个字符的数量
len++;
}
for (int i = 0; i < n - len; ++i) {
printf("%c", s[i]);
}
for (int i = 'a'; i <= 'z'; ++i) {
to[i] = 0;
if (num[i] % k) {
to[i] = k - (num[i] % k);
len -= to[i];
}
}
for (int i = 1; i <= len; ++i) printf("a");
for (int i = 'a'; i <= 'z'; ++i) {
while (to[i]) {
printf("%c", i);
to[i]--;
}
}
printf("\n");
}
int main() {
int _T = 1;
scanf("%d", &_T);
while (_T--) solve();
return 0;
}
/*
1
9 3
aabzzzzzz
*/
D. GCD of an Array
题意:
给你一个序列\(a_1\) ~ \(a_n\),每次操作将\(a_i\)元素增大\(x\)倍,输出每次操作后整个序列的\(gcd\)值 \(mod\) \(10^9 + 7\)。
思路:
我们考虑整个序列的\(gcd\)值,可以把它看成\({p_1}^{k_1} * {p_2}^{k_2} * ... * {p_m}^{k_m}\),其中\(p_i\)为所有元素都拥有的质因数,\(k_i\)为\(p_i\)这个质因数在所有值中拥有数量的最小值。由此也可以推出序列的\(gcd\)值是只增不减的。我们将初始答案置为\(1\),\(a_i\)也都置为\(1\),每次给\(a_i\)增加\(x\)倍时,对\(x\)进行质因数分解。假设我们当前分解出来的质因数为\(p\),如果\(a_i\)先前没有\(p\)这个质因数,那么对\(p\)的总体数量有一个贡献。当\(p\)的数量达到\(n\)时,说明每个\(a_i\)都拥有了\(p\)这个质因数,会对答案产生贡献。我们找出上一次\(p\)对答案的贡献\(k\)以及当前序列所有元素中\(p\)的最小数量,产生贡献的数量即为\(p\)的最小数量减去上一次\(p\)的贡献,将答案乘上这么多个\(p\)就行了。我们可以用\(map\)来记录\(a_i\)中每个质因数的数量,然后用\(multiset\)来维护序列所有元素中\(p\)的最小数量。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 7;
const ll mod = 1e9 + 7;
map<int, int> cntDiv[N];
multiset<int> cntTot[N];
int pre[N], tot[N], nxt[N], n, q, v, id;
ll ans = 1;
ll powmod(ll a, ll b) {
ll res = 1;
a %= mod;
for (; b; b >>= 1) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
}
return res;
}
void add(int k, int x) {
while (x != 1) {
int div = nxt[x], add = 0;
while (nxt[x] == div) add++, x /= nxt[x];
if (!cntDiv[k].count(div)) {
tot[div]++;
}
if (cntDiv[k][div]) {
cntTot[div].erase(cntTot[div].find(cntDiv[k][div]));
}
cntDiv[k][div] += add;
cntTot[div].insert(cntDiv[k][div]);
if (tot[div] == n) {
int num = *cntTot[div].begin() - pre[div];
ans = ans * powmod(div, num) % mod;
pre[div] = *cntTot[div].begin();
}
}
}
void solve() {
scanf("%d %d", &n, &q);
for (int i = 2; i < N; ++i) {
if (nxt[i] == 0) {
nxt[i] = i;
if (i > 10000) continue;
for (int j = i * i; j < N; j += i) {
if (nxt[j] == 0) nxt[j] = i;
}
}
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &v);
add(i, v);
}
for (int i = 1; i <= q; ++i) {
scanf("%d %d", &id, &v);
add(id, v);
printf("%lld\n", ans);
}
}
int main() {
int _T = 1;
while (_T--) solve();
return 0;
}