A - 윤년 계산
문제 요약
입력으로 받은 연도 n을 기준으로 해당 연도의 일수가 365 일인지 366 일인지 판단해야 합니다.
해결책
윤년 여부를 판별하는 논리식을 적용하면 됩니다. 일반적으로 다음 규칙이 성립합니다:
- 4 로 나누어 떨어지지 않는 경우 평년입니다.
- 400 으로 나누어 떨어지는 경우 윤횔입니다.
- 100 으로 나누어 떨어지지 않으면서 4 로 나누어 떨어지는 경우도 윤횔입니다.
위의 조건들을 조합하여 출력 값을 결정하면 됩니다.
코드 구현
#include<iostream>
#include<vector>
using namespace std;
void solve() {
int year;
if (!(cin >> year)) return;
int days = 365;
// 1. 400 의 배수인 경우
if (year % 400 == 0) {
days = 366;
}
// 2. 100 은 배수지만 400 은 아님 (이 경우 아래로 안감)
else if (year % 100 == 0) {
days = 365;
}
// 3. 4 의 배수이지만 100 은 아님
else if (year % 4 == 0) {
days = 366;
}
cout << days << "\n";
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
solve();
return 0;
}
B - 두 번째로 큰 값
문제 요약
정수 배열 $a_n$ 이 주어졌을 때, 가장 큰 수를 제외한 나머지 수들 중 최댓값인 "두 번째 큰 값"의 인덱스를 구하는 문제입니다.
해결책
한 번의 순회 (Single Pass) 를 통해 현재까지 발견된 최댓값과 두 번째 최댓값을 유지하면서 진행하면 $O(N)$ 시간에 해결할 수 있습니다.
- 현재 요소가 기존 최댓값보다 크면: 이전 최댓값을 두 번째 최댓값으로 올리고, 현재 요소를 새로운 최댓값으로 설정합니다.
- 현재 요소가 최댓값보다는 작지만 두 번째 최댓값보다는 크면: 두 번째 최댓값과 그 위치를 업데이트합니다.
코드 구현
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
if (!(cin >> n)) return 0;
vector<long long> nums(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> nums[i];
}
long long first_val = nums[1];
long long second_val = -1e18;
int first_idx = 1;
int second_idx = -1;
for (int i = 2; i <= n; ++i) {
if (nums[i] > first_val) {
second_val = first_val;
second_idx = first_idx;
first_val = nums[i];
first_idx = i;
} else if (nums[i] > second_val) {
second_val = nums[i];
second_idx = i;
}
}
cout << second_idx << "\n";
return 0;
}
C - 이동 비용 합계
문제 요약
$x$라는 변수를 사용하여 모든 항목에 대해 $\min(A_i, x)$ 의 합이 $M$ 이하가 되도록 하는 $x$ 의 최댓값을 구합니다. 만약 충분히 큰 $x$ 에도 조건이 만족된다면 무한대로 출력합니다.
해결책
함수 $f(x) = \sum \min(A_i, x)$ 는 $x$ 에 대해 단조 증가하는 특성을 가집니다. 따라서 $x$ 를 이진 탐색 (Binary Search) 하여 조건을 만족하는 최대 값을 찾을 수 있습니다.
- 우선 $x$ 가 매우 클 때 ($10^{17}$ 등) 합이 $M$ 이하인지 확인하여 무한대 여부와 무관한 경우를 처리합니다.
- 아니라면 범위를 $[0, M]$ 로 잡고 이진 탐색을 수행합니다.
- 검증 함수 (check function) 에서 주어진 $mid$ 에 대해 합계를 계산하여 조건을 체크합니다.
코드 구현
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n;
long long m_limit;
vector<long long> costs;
bool is_valid(long long val) {
long long total_cost = 0;
for (int i = 0; i < n; ++i) {
total_cost += min(costs[i], val);
if (total_cost > m_limit) return false;
}
return total_cost <= m_limit;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
if (!(cin >> n >> m_limit)) return 0;
costs.resize(n);
for (int i = 0; i < n; ++i) {
cin >> costs[i];
}
if (is_valid(200000000000000000LL)) {
cout << "infinite\n";
return 0;
}
long long low = 0, high = m_limit;
long long ans = 0;
while (low <= high) {
long long mid = low + (high - low) / 2;
if (is_valid(mid)) {
ans = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
cout << ans << "\n";
return 0;
}
D - 가위바위보 3
문제 요약
상대가 정해진 순서로 손 모양 ($S, P, R$) 을 낼 때, 상대방은 절대 패하지 않으며 연속된 두 번의 출수는 같을 수 없습니다. 이때 내가 얻을 수 있는 승리 횟수의 최댓값을 구해야 합니다.
해결책
이 문제는 전역적인 제약조건 때문에 단순히 Greedy 로 접근하기 어려우며, 상태 전이가 존재하므로 동적 계획법 (DP) 을 사용해야 합니다.
- 상태 정의: $dp[i][0]$ 은 $i$ 번째 라운드에서 내가 비겼을 때의 최대 승패 횟수, $dp[i][1]$ 은 $i$ 번째 라운드에서 내가 이겼을 때의 값입니다.
- 전이: 현재 선택한 손모양이 직전 회차의 손모양과 겹치지 않아야 합니다.
- 이기는 행: 상대의 $S$ 에서는 내 $P$, $R$ 에서는 내 $S$, $P$ 에서는 내 $R$ 입니다.
코드 구현
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
string opp_moves;
cin >> n >> opp_moves;
// dp[i][0]: non-win (win tie) count, dp[i][1]: win count
vector<vector<int>> dp(n, vector<int>(2, -1));
// Base case initialization is handled by transitions mostly
// We treat the first move specially or initialize carefully
int current_tie_move = -1;
int current_win_move = -1;
auto get_tie = [&](char op) { return op; };
auto get_win = [&](char op) {
if (op == 'S') return 'P';
if (op == 'P') return 'R';
return 'S';
};
for (int i = 0; i < n; ++i) {
char opponent = opp_moves[i];
// Possible moves we can make now to TIE or WIN
char tie_move = get_tie(opponent);
char win_move = get_win(opponent);
int prev_tie_score = 0;
int prev_win_score = -1e9;
if (i > 0) {
// Check previous state validity implicitly via logic or initialized values
// Here we simply assume accessing i-1
// But for code simplicity relative to original, let's stick to loop logic
}
// Initialize base state logic manually inside loop or separate
if (i == 0) {
dp[0][0] = 0;
dp[0][1] = 1;
} else {
// Logic derived from previous states
// If we want to tie now with `tie_move`:
// Previous could have been Tie (different move) or Win (different move)
// Determine scores if we pick TIE move
int t_score = -1e9;
if (i > 0) {
if (current_tie_move != tie_move && dp[i-1][0] >= 0)
t_score = max(t_score, dp[i-1][0]);
if (current_win_move != tie_move && dp[i-1][1] >= 0)
t_score = max(t_score, dp[i-1][1]);
}
// Determine scores if we pick WIN move (+1 score)
int w_score = -1e9;
if (i > 0) {
if (current_tie_move != win_move && dp[i-1][0] >= 0)
w_score = max(w_score, dp[i-1][0] + 1);
if (current_win_move != win_move && dp[i-1][1] >= 0)
w_score = max(w_score, dp[i-1][1] + 1);
}
// Update global prev moves for next iteration
current_tie_move = tie_move;
current_win_move = win_move;
}
// Correcting implementation to match previous flow properly for safety
// Resetting approach for clarity:
}
// Simplified logic rewrite matching core requirement
// Re-writing main DP logic cleanly
fill(dp.begin(), dp.end(), vector<int>(2, -1000000000));
// Initial State handling
// Round 0
if (opp_moves[0] == 'S') { /* tie=S, win=P */ }
else if (opp_moves[0] == 'R') { /* tie=R, win=S */ }
else { /* tie=P, win=R */ }
dp[0][0] = 0;
dp[0][1] = 1;
// Tracking last played moves
// 0: Rock(S), 1: Paper(P), 2: Scissor(R) mapping
// Using direct char comparison is easier with original logic style but renamed vars
char last_non_win = opp_moves[0];
char last_win = (opp_moves[0]=='S'?'P':(opp_moves[0]=='P'?'R':'S'));
for(int i=1; i
E - 이산 적분 (Xor Sigma)
문제 요약
주어진 배열 $A_N$ 에 대해, 모든 서브배열 $A_{i+1} \dots A_j$ 의 XOR 합을 모두 더한 결과 값을 구해야 합니다.
해결책
이 문제를 해결하기 위해서는 XOR 앞쪽 합 (Prefix XOR) 의 개념을 활용해야 합니다.区间 $[l, r]$ 의 XOR 합은 $Prefix[r] \oplus Prefix[l-1]$ 로 계산됩니다.
원래 식은 모든 쌍 $(i, j)$ 에 대한 XOR(prefix difference) 의 합입니다. 이를 확장하면, 모든 원소들의 XOR 합을 구하고, 서로 다른 prefix pair 들 간의 XOR 합을 구하여 결합한 후, 원래 단일 원소 $A_i$ 들의 총합을 빼주는 방식으로 접근할 수 있습니다.
또는 각 비트 별 기여도를 계산하는 방법으로도 최적화할 수 있습니다. 특정 비트 위치 $k$ 에서 1 인 숫자들의 개수를 세어 조합 수학을 적용하거나, 직접 구간 XOR 합들을 모두 계산하여 누적하는 방식입니다.
코드 구현
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
if (!(cin >> n)) return 0;
vector<long long> b(n + 1);
vector<long long> pre_xor(n + 1, 0);
for (int i = 1; i <= n; ++i) {
cin >> b[i];
pre_xor[i] = pre_xor[i-1] ^ b[i];
}
// Calculate sum of all single elements (to subtract later)
long long initial_sum_elements = 0;
for (int i = 1; i <= n; ++i) initial_sum_elements += b[i];
// The result consists of:
// 1. Sum of all prefix XORs themselves
// 2. Sum of pairwise XORs between prefix sums
long long final_res = 0;
// Part 1: Sum of individual prefix XORs
for (int i = 1; i <= n; ++i) {
final_res += pre_xor[i];
}
// Part 2: Sum of pairwise XORs (similar to original "Two XOR" problem logic)
// Count set bits for each position across all prefix sums
vector<int> bit_counts(20, 0);
long long mx_bits = 0;
for (int i = 1; i <= n; ++i) {
long long val = pre_xor[i];
for(int k=0; k<20; ++k){
if ((val >> k) & 1) bit_counts[k]++;
}
}
for (int k = 0; k < 20; ++k) {
int count_ones = bit_counts[k];
long long count_zeros = n - count_ones;
// Contribution to pairwise XOR sum: ones * zeros * 2^k
final_res += (long long)count_ones * count_zeros * (1LL << k);
}
// Subtract the sum of original elements as derived from formula logic
final_res -= initial_sum_elements;
cout << final_res << "\n";
return 0;
}