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 번째로 주어진 값을 Xi라 하면, Ai=X⊕Xi 가 됨을 확인할 수 있다.
#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)의 문자열의 개수를 구하는 문제이다. 단, 사용할 수 있는 문자들의 집합 Σ=a,b,…,z 이다.
문제를 바꾸어서 길이 N인 문자열 중 S를 부분문자열로 포함하지 않는 문자열의 수를 구해보자. 한 가지 좋은 점근은 Ai := (부분문자열인 S의 접두사 중 가장 긴 것의 길이가 i인 길이 N의 문자열의 집합) 로 정의할 때, 이 |Ai| 들에 대한 명확한 식을 찾아내는 것이다.
그런데, Ai에 속하는 문자열들을 다음의 규칙을 통해 만들어낼 수 있음을 확인할 수 있다.
- 1,2,…,N 가운데 S1과 같은 문자를 가지는 가장 빠른 인덱스 k1, k1 이후 인덱스 가운데 S2와 같은 문자를 가지는 인덱스 k2, ... 와 같은 식으로 i개의 인덱스 k1<k2<⋯<ki를 잘 골라준다.
- kj 에는 문자 Sj를 배정한다.
- 이후 나머지 인덱스에 문자를 배정하는데, 인덱스 k′이 kj<k′<kj+1일 때 (단, ki+1=∞) k′ 번째 위치에는 Sj+1이 아닌 문자 아무거나 배정한다.
결국 |Ai| = (kj들에 문자를 배정하는 경우의 수 ) × (나머지 문자 배정하는 경우의 수) 가 되므로,
|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) (1) | 2020.06.25 |
2020 UCPC 예선 대비 개인 연습 (1) (1) | 2020.06.16 |
2019 ICPC 한국 리저널 인터넷예선 풀이 (0) | 2020.06.03 |
Google Code Jam Round 1C (0) | 2020.05.03 |