Codeforces Round 867 (Div. 3)
A. TubeTube Feed (800)Permalink
지문이 좀 읽기 어려운데 결론적으로 인 것 중 가장 큰 를 찾는 문제이다.
B. Karina and Array (800)Permalink
정렬하고 가장 작은 것 두개의 곱이나 가장 큰 것 두개의 곱이 정답이다.
음수 * 음수 = 가장큰 양수가 될 수 있기 때문이다.
C. Bun Lover (800)Permalink
에디토리얼은 다른 풀이를 갖지만, 내가 푼 풀이는 다음과 같다.
잘 살펴보면 이라는 점화식을 세울 수 있다.
임을 알 수 있으므로 바로 계산해줄 수 있다.
D. Super-Permutation (1200)Permalink
일땐 항상 가능하다.
이 짝수일 때 항상 만들 수 있는 방법은 다음과 같다.
n 1 n-2 3 n-4 5 n-6 ... 2 n-1
와 같이 구성하면 된다.
이 이 아닌 홀수일 땐 불가능하다.
가 에서 이 있는 자리라고 하자.
이 되면
이 되기 때문에 은 에서 항상 첫 자리를 가지고 있다. 즉, 이다.
홀수일 때를 보자.
이다. 이란 뜻이고 이 되어버렸으므로 불가능하다.
void solve() {
int n;
cin >> n;
if (n == 1) {
cout << 1 << endl;
} else if (n % 2 == 1) {
cout << -1 << endl;
} else {
vi ans(n + 1);
for (int i = 1; i <= n; i += 2) ans[i] = i;
for (int i = n; i >= 0; i -= 2) ans[n - i] = i;
for (int i = 0; i < n; i++) cout << ans[i] << ' ';
cout << endl;
}
}
E. Making Anti-Palindromes (1600)Permalink
일단 홀수면 불가능하다.
동일한 알파벳이 개보다 많으면 항상 인 부분이 생기므로 불가능하다.
그렇지 않다고 하자.
인 쌍을 발견했을 때 와 중 작은 알파벳의 개수를 세주자.
모든 알파벳의 세진 개수를 배열에 넣고 정렬한다. 이 배열을 라 한다.
번의 교체로 항상 인 쌍을 두 개나 한 개를 제거할 수 있다.
최대한 두 개를 제거하는걸 많이 쓴 다음 한 개로 제거해야 하는 것을 제거해야 한다.
다음과 같이 쌍이 있다고 하자.
s=cbaaaaaabc
a - a
a - a
a - a
b - b
c - c
이 경우 정답은 인데 왜그런지 보자.
이 된다.
서로 다른 알파벳을 바꾸는 경우 항상 지워야 할 것이 개씩 줄어든다. 가령 다음과 같다.
이제 나머지 는 한 개 혼자 남았으므로 항상 번의 스왑으로 한개만 삭제해줄 수 있다.
이런식으로 에서 가장 큰 수와 나머지 것들의 합을 비교하면 된다.
라면 정답은 이고, 그렇지 않다면 정답은 이다.
void solve() {
int n;
string s;
cin >> n >> s;
if (n % 2 == 1) {
cout << -1 << endl;
return;
}
vi cnt(26);
for (char c: s) cnt[c - 'a']++;
for (int i = 0; i < 26; i++) {
if (cnt[i] > n / 2) {
cout << -1 << endl;
return;
}
}
vi c(26);
for (int i = 0; i < n / 2; i++) {
if (s[i] == s[n - 1 - i]) {
c[min(s[i], s[n - 1 - i]) - 'a']++;
}
}
vi x;
for (int i = 0; i < 26; i++) if (c[i]) x.pb(c[i]);
sort(all(x));
int sum = acc(x);
if (sum == 0) {
cout << 0 << endl;
return;
}
int other = sum - x.back();
if (x.back() > other) {
cout << x.back() << endl;
} else {
cout << (sum + 1) / 2 << endl;
}
}
F. Gardening Friends (1700)Permalink
전형적인 re-rooting 테크닉을 쓰는 문제여서 설명을 생략한다.
G1. Magic Triples (Easy Version) (1700)Permalink
G1. Magic Triples (Easy Version)
이므로 라 할 때, 은 이기 때문에 인 까지만 검사를 해줄 수 있다.
수를 세는건 단순히 경우의 수를 세면 된다.
이는 시간복잡도 이 걸릴 것이다. 시간내에 충분히 통과 가능하다.
const int MAX = 1e6;
const int SQ = 1e3 + 100;
int cnt[MAX + 1];
void solve() {
int n;
cin >> n;
vi a(n);
fv(a);
sort(all(a));
for (int i = 0; i < n; i++) cnt[a[i]]++;
ll ans = 0;
for (int b = 1; b <= SQ; b++) {
for (int i = 0; i < n; i++) {
if (a[i] % (b * b) == 0) {
if (i == n - 1 || a[i] != a[i + 1]) {
if (b >= 2) {
int X = a[i] / b / b;
int Y = a[i] / b;
ans += 1ll * cnt[a[i]] * cnt[X] * cnt[Y];
} else {
ans += 1ll * cnt[a[i]] * (cnt[a[i]] - 1) * (cnt[a[i]] - 2);
}
}
}
}
}
cout << ans << endl;
for (int i: a) cnt[i] = 0;
}
G2. Magic Triples (Hard Version) (2200)Permalink
스킵하려다가 에디토리얼을 보고 어떻게 푸는지 공부했다.
경우를 구간으로 나눠서 세는 테크닉은 특이해서 기억할만 했다.
G2. Magic Triples (Hard Version)
다음과 같은 경우를 순서대로 각각 세준다.
각각 1,2,3 번이라고 하면
배열에 어떤 수가 몇개 있는지 판단하는 시간복잡도를 무시해서 이라 할 때,
1번은 에 가능하고,
2번은 이고 의 후보가 최대 개라서 이면 된다.
3번은 이기 때문에 의 후보를 이 아닌 의 약수로 제한할 수 있기 때문에 이고 약수들은 로 찾을 수 있어서 결국 인 는 많아봤자 개이기 때문에 에 해결된다.
따라서 에 모두 검사할 수 있고, 해결 가능하다.
는 배열에 어떤 수가 몇개있는지 세는 시간복잡도이다. 이 될 것이다.
const int MAX = 1e9;
void solve() {
int n;
cin >> n;
vi a(n);
fv(a);
sort(all(a));
auto cnt = [&](int x) {
return ubi(a, x) - lbi(a, x);
};
ll ans = 0;
for (int i = 0; i < n; i++) {
if (i == n - 1 || a[i] != a[i + 1]) {
ll c = cnt(a[i]);
ans += c * (c - 1) * (c - 2);
}
}
auto count = [&](ll aj, int b) -> ll {
if (aj % b) return 0;
int x = aj / b;
ll z = aj * b;
if (z > MAX) return 0;
int xc = cnt(x);
if (!xc) return 0;
int zc = cnt(z);
return 1ll * xc * zc;
};
for (int i = 0; i < n; i++) {
if (a[i] >= 1e6) {
for (int b = 2; b <= 1e3; b++) {
ans += count(a[i], b);
}
} else {
for (int b = 1; b * b <= a[i]; b++) {
if (a[i] % b == 0) {
if (b != 1) {
ans += count(a[i], b);
}
int b2 = a[i] / b;
if (b2 != b && b2 != 1) {
ans += count(a[i], b2);
}
}
}
}
}
cout << ans << endl;
}
Comments