AtCoder Beginner Contest 171
문제 풀이
D. Replacing
$A$의 원소들의 합을 관리하는 배열과 $X[x]$ := ($A$에 $x$가 등장하는 횟수) 로 정의된 배열 $X$를 잘 관리해주면 된다.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N, Q;
ll X[100005];
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N;
ll sum = 0;
for (int i=0; i<N; i++){
int x; cin >> x;
X[x]++;
sum += x;
}
cin >> Q;
while (Q--){
int b, c;
cin >> b >> c;
sum -= b * X[b];
sum += X[b] * c;
X[c] += X[b];
X[b] = 0;
cout << sum << '\n';
}
}
E. Red Scarf
어떤 수를 홀수번 XOR 하면 원래의 수와 같고, XOR 연산들은 순서에 상관없음을 이용하면 된다. 문제에서 주어진 값들을 모두 XOR 한 값을 $X$, $i$ 번째로 주어진 값을 $X_i$라 하면, $A_i = X \oplus X_i$ 가 됨을 확인할 수 있다.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N;
int A[200005];
int X;
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N;
for (int i=0; i<N; i++){
cin >> A[i];
X ^= A[i];
}
for (int i=0; i<N; i++) cout << (X ^ A[i]) << ' ';
cout << endl;
}
F. Strivore
문제를 요약하자면, 어떤 문자열 $S$가 주어지면, $S$를 부분문자열로 하는 길이 $N(=|S|+K)$의 문자열의 개수를 구하는 문제이다. 단, 사용할 수 있는 문자들의 집합 $\Sigma = { a, b, \dots, z}$ 이다.
문제를 바꾸어서 길이 $N$인 문자열 중 $S$를 부분문자열로 포함하지 않는 문자열의 수를 구해보자. 한 가지 좋은 점근은 $A_i$ := (부분문자열인 $S$의 접두사 중 가장 긴 것의 길이가 $i$인 길이 $N$의 문자열의 집합) 로 정의할 때, 이 $|A_i|$ 들에 대한 명확한 식을 찾아내는 것이다.
그런데, $A_i$에 속하는 문자열들을 다음의 규칙을 통해 만들어낼 수 있음을 확인할 수 있다.
- $1, 2, \dots, N$ 가운데 $S_1$과 같은 문자를 가지는 가장 빠른 인덱스 $k_1$, $k_1$ 이후 인덱스 가운데 $S_2$와 같은 문자를 가지는 인덱스 $k_2$, ... 와 같은 식으로 $i$개의 인덱스 $k_1 < k_2 < \dots < k_i$를 잘 골라준다.
- $k_j$ 에는 문자 $S_j$를 배정한다.
- 이후 나머지 인덱스에 문자를 배정하는데, 인덱스 $k'$이 $k_j < k' < k_{j+1}$일 때 (단, $k_{i+1} = \infty$) $k'$ 번째 위치에는 $S_{j+1}$이 아닌 문자 아무거나 배정한다.
결국 $|A_i|$ = (${ k_j }$들에 문자를 배정하는 경우의 수 ) × (나머지 문자 배정하는 경우의 수) 가 되므로,
$$
|A_i| = {N \choose i} (|\Sigma | - 1) ^ {N - i}
$$
이 된다. 문제의 답은 $|\Sigma|^N - \sum_{i=0}^{|S|-1} |A_i|$ 가 된다.
사족. 개인적으로는 생각보다 어려운 문제였다. 푸는데 꽤나 오래 걸렸던 것 같다.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1'000'000'007;
ll powmod(ll x, ll n){
x %= mod;
ll res = 1;
while (n){
if (n & 1) res = (res * x) % mod;
n >>= 1;
x = (x * x) % mod;
}
return res;
}
inline ll submod(ll x, ll y){
x %= mod; y %= mod;
return (x + mod - y) % mod;
}
inline ll addmod(ll x, ll y){
x %= mod; y %= mod;
return (x + y) % mod;
}
inline ll mulmod(ll x, ll y){
x %= mod; y %= mod;
return (x * y) % mod;
}
ll K, N;
string S;
ll fac[2000006], ifac[2000006];
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> K;
cin >> S; N = S.length() + K;
ll ans = powmod(26, N);
fac[0] = 1;
for (ll i=1; i<=2000000; i++) fac[i] = mulmod(fac[i-1], i);
ifac[2000000] = powmod(fac[2000000], mod-2);
for (ll i=2000000; i; i--) ifac[i-1] = mulmod(ifac[i], i);
for (ll i=0; i<S.length(); i++){
ll cur = powmod(25, N-i);
ll binom = mulmod(mulmod(fac[N], ifac[i]), ifac[N-i]);
cur = mulmod(binom, cur);
ans = submod(ans, cur);
}
cout << ans << endl;
}
Codeforces Round #651
문제 풀이
C. Number Game
경우를 다음과 같이 나누어 생각해볼 수 있다.
- $N$이 홀수일 때:
- $N = 1$인 경우: 선수가 진다.
- $N$이 $1$보다 큰 홀수인 경우: 선수가 $N /= N$을 할 수 있으므로, 선수가 이긴다.
- $N$이 짝수일 때:
- $N = 2$인 경우: 선수가 $N -= 1$을 할 수 있으므로, 선수가 이긴다.
- $N = 2p$ ($p$는 홀수소수)인 경우: 선수가 $N /= p$와 $N -= 1$ 중 어느 것을 택해도 진다.
- $N = 2^k$ ($k > 1$)인 경우: 선수는 $N -= 1$을 강제로 선택하게 되고, 진다.
- 이 외의 경우, 선수가 $N$을 ($N$의 모든 odd prime power의 곱) 으로 나누면 이긴다.
개인적으로, 대회 중에 케이스 분류하는게 약간은 까다로웠다.
D. Odd-Even Subsequence
답에 대한 이분탐색을 통해 문제를 해결할 수 있다. $a_1$을 포함하는 subsequence만 고려해도 문제의 답이 바뀌지 않음을 알 수 있다. 자세한 것은 코드를 참고하자.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N, K;
int A[200005];
vector<int> I;
bool able(int X){
I.clear();
I.push_back(A[1]);
if (I[0] <= X){
int j = 1;
for (int i=2; i<=N; i++){
if (j % 2 == 0){
if (A[i] <= X){
I.push_back(A[i]);
j++;
}
continue;
}
I.push_back(A[i]);
j++;
continue;
}
if ((int) I.size() >= K) return true;
}
I.clear(); I.push_back(A[1]);
int j = 1;
for (int i=2; i<=N; i++){
if (j % 2 == 1){
if (A[i] <= X){
I.push_back(A[i]);
j++;
}
continue;
}
I.push_back(A[i]);
j++;
continue;
}
return ((int) I.size()) >= K;
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N >> K;
for (int i=1; i<=N; i++){
cin >> A[i];
}
int lo = 0, hi = 1E9;
while (lo + 1 < hi){
int mid = (lo + hi) / 2;
if (able(mid)) hi = mid;
else lo = mid;
}
cout << hi << endl;
}
E. Binary Sequence Rotation
먼저, $s$와 $t$의 $0$, $1$의 개수가 같은 경우만 고려해도 충분함을 확인할 수 있다. 또한, $s_i = t_i$인 인덱스들을 전부 제거하고 고려해도 무방하다.
최적 횟수의 연산으로 두 문자열을 같게 만드는 방법 중 각 연산에서 택한 $s$의 인덱스들에 있는 문자가 $1010\dots$ 내지는 $0101 \dots$와 같이 alternating 하게 하는 방법이 있음을 확인할 수 있다.
결국 이 문제는 어떤 binary 문자열을 최소 개수의 alternating subsequence로 cover하는 문제가 되고, 이는 $1$이 있는 인덱스에는 $+1$, $0$이 있는 인덱스에는 $-1$로 이루어진 배열의 최대/최소 부분합을 구해서 해결할 수 있다.
대회 끝나고 업솔빙을 통해 해결한 문제이다.
Codeforces Round #649
문제 풀이
C. Ehab and Prefix MEX's
핵심이 되는 관찰은 $a_i \neq a_{i-1}$이면, $b_i = a_{i-1}$라는 것이다. 이를 이용해서 잘 구현해주면 되는데, 나는 잘 구현하는게 생각보다 어려웠다. 구현은 생각보다 짧다.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N;
int A[100005];
int B[100005];
bool used[100005];
int mx;
stack<int> idx;
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N;
for (int i=1; i<=N; i++) cin >> A[i];
bool flag = true;
memset(B, -1, sizeof(B));
for (int i=1; i<=N; i++){
idx.push(i);
if (A[i] == A[i-1]) continue;
for (int j=A[i-1]; j<=A[i]-1; j++){
int k = idx.top();
idx.pop();
B[k] = j;
}
}
for (int i=1; i<=N; i++){
if (B[i] != -1) break;
B[i] = N+5;
}
for (int i=1; i<=N; i++){
if (B[i] == -1) B[i] = B[i-1];
}
for (int i=1; i<=N; i++) cout << B[i] << ' ';
cout << endl;
}
D. Ehab's Last Corollary
둘 중 하나를 찾을 수 있음이 주어졌으므로, 어느 하나를 찾으려고 시도해보고, 실패할 경우 그 과정에서 얻은 정보로 다른 하나를 효율적으로 찾는 접근을 해볼 수 있다.
어떤 정점 $s$를 시작으로 해서 BFS를 해보자. BFS에서 $1, 2, \dots, k$ 번째로 방문한 정점을 고르자. 고른 정점 가운데 짝수인 정점들의 집합을 $X$, 홀수인 정점들의 집합을 $Y$라 할 때, 비둘기 집의 원리에 의해 둘 중 어느 하나는 $\displaystyle \lceil { k \over 2} \rceil$ 이상이다.
만약, 그래프의 간선 가운데 $X$에 속하는 두 정점을 잇는 간선이 없다면, 우리는 task 1을 해결한 것이다.
아니라고 가정하자. 그러한 간선 $u \to v$가 있다고 하자. 그러면, $u \to v$ 간선을 제외한 나머지 간선만 활용하며 $u$로부터 BFS를 수행하여 $v$로 가는 경로 $P_{uv}$를 찾자. 앞서 밑줄 친 내용에 의해 $u$와 $v$ 사이의 거리는 $k$ 미만임이 보장된다. 따라서, $P_{uv}$에 간선 $u \to v$를 더해주면 크기 $k$ 이하의 사이클이 되므로 task 2에 대한 답이 된다.
'Computer Science > Competition' 카테고리의 다른 글
UCPC 2020 참가 후기 (0) | 2020.08.01 |
---|---|
2020 UCPC 예선 대비 개인 연습 (3) (0) | 2020.06.25 |
2020 UCPC 예선 대비 개인 연습 (1) (0) | 2020.06.16 |
2019 ICPC 한국 리저널 인터넷예선 풀이 (0) | 2020.06.03 |
Google Code Jam Round 1C (0) | 2020.05.03 |