티스토리 뷰

대회반

앳코더 398

탁택 2025. 3. 25. 01:51

A

더보기
  • 출력해야 하는 문자열 규칙:
    • $N$이 짝수인 경우:
      문자열의 중앙에 두 개의 '=' 기호가 위치하며, 그 좌우에 '-' 기호가 배치됩니다.
      전체 '-' 기호의 개수는 $N-2$개이며, 좌우에 균등하게 분배됩니다.
      즉, 왼쪽에 $\frac{N-2}{2}$개의 '-'와 오른쪽에 $\frac{N-2}{2}$개의 '-'를 붙이고, 그 사이에 "=="를 넣습니다.
    • $N$이 홀수인 경우:
      문자열의 중앙에 한 개의 '=' 기호가 위치하며, 그 좌우에 '-' 기호가 배치됩니다.
      전체 '-' 기호의 개수는 $N-1$개이며, 좌우에 균등하게 분배됩니다.
      즉, 왼쪽에 $\frac{N-1}{2}$개의 '-'와 오른쪽에 $\frac{N-1}{2}$개의 '-'를 붙이고, 그 사이에 "="를 넣습니다.
  • 문제:
    정수 $N$이 주어졌을 때, 아래 규칙에 따라 문자열을 출력하는 문제입니다.
#include<bits/stdc++.h>
using namespace std;

int main(){
  int n;
  cin >> n;

  if(n%2 == 0){
    int c = (n-2)/2;
    cout << string(c,'-') + "==" + string(c,'-') << endl;
  }else{
    int c = (n-1)/2;
    cout << string(c,'-') + "=" + string(c,'-') << endl;
  }
}
N = int(input())

if N % 2 == 0:
    print('-' * (N // 2 - 1) + '=' * 2 + '-' * (N // 2 - 1))
else:
    print('-' * (N // 2) + '=' + '-' * (N // 2))

B

더보기

풀하우스 구성 조건

  1. 각 카드의 숫자 빈도수 계산
    • 주어진 카드들에서 각 숫자가 몇 장씩 있는지 셉니다.
  2. 전체 하우스를 만들 수 있는 조건
    전체 하우스는 오직 다음 두 조건을 동시에 만족할 때만 구성할 수 있습니다:
    • 세 장 이상의 같은 숫자:
      카드 중 하나의 숫자 $x$가 적힌 카드가 최소 3장 이상 있어야 합니다.
    • 두 장 이상의 다른 숫자:
      $x$와 다른 숫자 $y$가 적힌 카드가 최소 2장 이상 있어야 합니다.
  3. 빈도수 정렬로 검증하는 방법
    • 각 숫자의 빈도수를 내림차순으로 정렬한 후,
      • 가장 많이 등장한 숫자의 빈도가 3 이상이고,
      • 두 번째로 많이 등장한 숫자의 빈도가 2 이상이면 전체 하우스를 만들 수 있습니다.
#include<bits/stdc++.h>

using namespace std;

int main(){
  vector<int> bk(13,0);
  for(int i=0;i<7;i++){
    int x;
    cin >> x;
    bk[x-1]++;
  }
  sort(bk.rbegin(),bk.rend());
  if(bk[0]>=3 && bk[1]>=2){cout << "Yes\n";}
  else{cout << "No\n";}
  return 0;
}

저 같은 경우 

0    ans 는 [3개 이상이 있는가?, 2개 이상이 있는가?] 으로 만들어 둡니다.
1    빈도수 저장 후 3개 이상인 카드를 찾고 ans[0] = True

2    2개 이상인데 3개 이상인걸 이미 찾았다면 3개 이상인지 볼 필요가 없겠죠. ans[1] = True

import sys

input = sys.stdin.readline

arr = list(map(int, input().split()))

cnt = [0] * 14

for e in arr:
    cnt[e] += 1

ans = [False, False]
for i in range(14):
    if cnt[i] >= 2:
        if cnt[i] >= 3 and not ans[0]:
            ans[0] = True
        else:
            ans[1] = True
if ans == [True, True]:
    print("Yes")
else:
    print("No")

C

더보기

문제 요약

총 $N$명의 사람이 있으며, 각 사람은 $1$부터 $N$까지의 라벨을 갖습니다.
각 사람 $i$는 정수 $A_i$를 가지고 있습니다.

조건:
다른 $N-1$명의 사람들 중 어떤 사람도 자신의 정수 $A_i$와 같은 정수를 갖지 않는 사람들만을 고려합니다.

목표:
조건을 만족하는 사람들 중, 정수 값이 가장 큰 사람의 라벨을 출력합니다.
만약 조건을 만족하는 사람이 없다면, 그 사실을 출력합니다.


초기 구현에서는 각 사람마다 다른 사람들과 비교하여 같은 숫자가 있는지를 확인하므로, 전체 시간 복잡도가 $ \Theta(N^2) $가 되어 큰 입력에서는 TLE(시간 초과)가 발생합니다.
함수형 프로그래밍의 함수(filter, all 등)를 사용해도 내부적으로 이중 반복문과 같은 동작을 하므로 동일한 시간 복잡도를 갖게 됩니다.

대신, 각 정수에 대해 해당 정수를 가진 사람들의 인덱스를 저장하는 associative array(예: 딕셔너리)를 구성하는 방법을 사용할 수 있습니다.
주어진 배열을 한 번 순회하면서 각 정수에 해당하는 인덱스를 추가하는 과정은 $ O(N \log N) $의 시간에 처리할 수 있으며, 해시를 사용할 경우 평균 $ O(N) $의 시간에 처리할 수 있습니다.

이 associative array에서, 리스트의 크기가 $1$인 경우에만 해당 정수가 다른 사람들과 중복되지 않았음을 의미합니다.
따라서, 딕셔너리의 내용을 한 번 순회하여 원하는 조건을 만족하는 인덱스를 찾으면, 전체 알고리즘의 시간 복잡도는 $ O(N \log N) $ 내에 해결할 수 있습니다.

from collections import defaultdict

N=int(input())
A=list(map(int,input().split()))

d=defaultdict(list)
for i,a in enumerate(A):
  d[a].append(i)

for key in sorted(d, reverse=True):
  if len(d[key])==1:
    print(d[key][0]+1)
    exit()

print(-1)

 

D

더보기

문제 요약

무한히 큰 2차원 격자에 캠프파이어가 위치한 $(0,0)$ 셀이 있으며, 시간 $t=0$에는 오직 $(0,0)$에만 연기가 존재합니다.

문자열 $S$가 주어지며, $S$는 길이 $N$의 문자열로 각 문자는 $N$, $W$, $S$, $E$ 중 하나입니다. 시간 $t=1,2,\dots,N$에 대해 다음과 같이 순차적으로 진행됩니다:

  1. 바람에 의한 연기의 이동:
    • 만약 $S_t = N$이면, 모든 셀 $(r,c)$의 연기가 $(r-1,c)$로 이동합니다.
    • 만약 $S_t = W$이면, 연기는 $(r,c-1)$로 이동합니다.
    • 만약 $S_t = S$이면, 연기는 $(r+1,c)$로 이동합니다.
    • 만약 $S_t = E$이면, 연기는 $(r,c+1)$로 이동합니다.
  2. 연기의 보충:
    • 만약 이동 후 $(0,0)$ 셀에 연기가 없다면, $(0,0)$에 새로운 연기를 생성합니다.

다카하시는 셀 $(R,C)$에 위치해 있습니다.

목표:
각 시간 $t+0.5$ (즉, 시간 $t$ 후 연기의 이동 및 보충이 끝난 직후) 시점에 셀 $(R,C)$에 연기가 존재하는지 여부를 판별하여, 문제에서 요구하는 형식에 따라 결과를 출력하는 것입니다.


 

연기를 바람에 따라 직접 이동시키면, 매 시간 $t$마다 연기를 이동하는 데 $O(t)$의 시간이 소요되어 전체 시간 복잡도가 $O(N^2)$가 됩니다. 이는 실행 시간 제한을 초과할 수 있습니다.

이를 피하는 방법은 다음과 같습니다:

  • 모든 연기는 바람에 따라 동일하게 움직이므로, 연기를 실제로 이동시키지 않고 연기가 생성되는 위치에 고정시킵니다.
  • 대신, 불과 다카하시의 위치를 바람의 반대 방향으로 이동시키면 두 객체 간의 상대적 위치는 원래 문제와 동일하게 유지됩니다.

구체적인 단계는 다음과 같습니다:

  1. 셀 $(0,0)$에 연기를 생성합니다.
  2. 불의 위치와 다카하시의 위치를 각각 $(0,0)$과 $(R,C)$로 초기화합니다.
  3. 시간 $t=1,2,\dots,N$에 대해, 아래의 규칙에 따라 불과 다카하시의 위치를 이동시킵니다:
    • 만약 $S_i = N$이면, 현재 위치 $(r, c)$에서 $(r+1, c)$로 이동합니다.
    • 만약 $S_i = W$이면, $(r, c)$에서 $(r, c+1)$로 이동합니다.
    • 만약 $S_i = S$이면, $(r, c)$에서 $(r-1, c)$로 이동합니다.
    • 만약 $S_i = E$이면, $(r, c)$에서 $(r, c-1)$로 이동합니다.
  4. 이동 후, 불이 위치한 셀에 연기를 생성합니다.
  5. 다카하시의 현재 위치에 연기가 있는지 확인하여, 있으면 $1$, 없으면 $0$을 출력합니다.

자료구조 활용:
연기가 생성된 셀의 위치들을 $set$과 같은 자료구조에 저장하면, 다카하시의 위치가 연기 위치 집합에 포함되어 있는지 빠르게 확인할 수 있습니다. 이 경우, 각 확인 연산의 시간 복잡도는 $O(\log N)$가 되므로, 전체 알고리즘의 시간 복잡도는 약 $O(N \log N)$로 줄어듭니다.

즉, "연기를 바람에 따라 이동시키는 대신 불과 다카하시를 반대 방향으로 이동시키는" 방법을 사용하면 실행 시간 제한 내에 문제를 해결할 수 있습니다.

#include<bits/stdc++.h>

using namespace std;
using pi=pair<int,int>;

int main(){
  int n;
  pi t;
  string s;
  cin >> n >> t.first >> t.second;
  cin >> s;
  pi f={0,0};
  set<pi> st;
  st.insert(f);

  for(auto &nx : s){
    if(nx=='N'){
      t.first++;
      f.first++;
    }
    else if(nx=='W'){
      t.second++;
      f.second++;
    }
    else if(nx=='S'){
      t.first--;
      f.first--;
    }
    else{
      t.second--;
      f.second--;
    }
    
    st.insert(f);
    if(st.find(t)==st.end()){cout << "0";}
    else{cout << "1";}
  }cout << "\n";
  return 0;
}
import sys

input = sys.stdin.readline

N, R, C = map(int, input().split())
S = input().strip()

x, y = 0, 0
dx, dy = [+1, 0, -1, 0], [0, +1, 0, -1]
mp = {}
s = []
for e in S:
    if e == 'N':
        s.append(0)
    elif e == 'W':
        s.append(1)
    elif e == 'S':
        s.append(2)
    else:
        s.append(3)

for e in s:
    if not ((x, y) in mp):
        mp[(x, y)] = 1
    x += dx[e]
    y += dy[e]
    R += dx[e]
    C += dy[e]
    if (R, C) in mp:
        print(1, end='')
    else:
        print(0, end='')
    # print(mp)
    # print(R, C)

E

더보기

홀수 사이클이 없는 그래프는 이분 그래프라고 하며, 이는 다음과 같이 설명할 수 있습니다:

  • 정점들을 두 가지 색으로 칠할 때, 같은 색의 정점끼리 연결된 간선이 없으면 그 그래프는 이분 그래프입니다.
  • 따라서, 정점 집합을 색상에 따라 두 개의 부분 집합(파티트 집합)으로 분할할 수 있습니다.
  • 연결된 이분 그래프의 경우, 이 두 부분 집합은 DFS(깊이 우선 탐색)나 BFS(너비 우선 탐색)를 통해 임의의 정점에서 시작하여 구할 수 있으며, 유일하게 결정됩니다.

문제 해결 아이디어

문제에서는 트리가 주어지고, 트리는 이분 그래프임을 이용합니다.
만약 두 부분 집합의 크기가 각각 $A$와 $B$라면, 트리에는 이미 $N-1$개의 간선이 존재합니다.
추가로 연결할 수 있는 간선의 최대 개수는
AB−(N−1)AB - (N-1)
개가 됩니다.

간선을 추가하는 순서는 간선을 추가할 수 있는지 여부에 영향을 주지 않으므로,
이 게임은 결국 AB−(N−1)AB - (N-1)개의 선택지 중 번갈아 가며 한 가지씩 선택하는 것과 동일합니다.
따라서, 만약 AB−(N−1)AB - (N-1)이 홀수라면 선공이 승리하고, 짝수라면 후공이 승리하게 됩니다.

문제는 각 정점이 어떤 부분 집합에 속하는지를 먼저 결정한 후, 가능한 간선 선택지를 모두 열거하여 set과 같은 자료구조에 저장하는 방식으로 해결할 수 있습니다.


추후 코드 추가

F

더보기

문제 요약

주어진 문자열 $S$를 접두사로 갖는 가장 짧은 팰린드롬을 하나 구하는 문제입니다.

입력 조건:

  • $S$는 길이가 $1$ 이상 $500,000$ 이하인 문자열입니다.
  • 문자열은 대문자 영어 알파벳으로 구성됩니다.

출력:

  • $S$를 접두사로 갖는 가장 짧은 팰린드롬 문자열을 출력합니다.

문자열 $S$와 그 역순 문자열의 연결은 당연히 팰린드롬이므로, 구하려는 문자열의 길이는 최대 $2 \times |S|$가 됩니다.

팰린드롬의 성질을 살펴보면, 길이가 $|S|+k$인 팰린드롬(여기서 $0 \le k \le |S|$)의 접두사가 $S$인 경우, 이를
S1S2…S∣S∣T1T2…TkS_1 S_2 \dots S_{|S|} T_1 T_2 \dots T_k
라고 나타낼 수 있습니다. 팰린드롬의 정의에 따라 아래와 같은 식들이 성립합니다.

S1=Tk,S2=Tk−1,    ⋮Sk=T1,Sk+1=S∣S∣,    ⋮S⌊(∣S∣+k+1)/2⌋=S⌊(∣S∣+k+2)/2⌋.\begin{aligned} S_1 &= T_k, \\ S_2 &= T_{k-1}, \\ &\;\;\vdots \\ S_k &= T_1, \\ S_{k+1} &= S_{|S|}, \\ &\;\;\vdots \\ S_{\lfloor(|S|+k+1)/2\rfloor} &= S_{\lfloor(|S|+k+2)/2\rfloor}. \end{aligned}

즉, $S$의 마지막 $(|S|-k)$개의 문자가 팰린드롬을 이루어야 하며, $T_1, T_2, \dots, T_k$는 $S$에 의해 유일하게 결정됩니다.

따라서, 문제는 $S$의 가장 긴 접미사가 팰린드롬인지 찾는 문제로 귀결됩니다.

이를 해결하는 직접적인 방법 중 하나가 바로 Manacher의 알고리즘으로, 이 알고리즘을 사용하면 문제를 $O(N)$ 시간에 해결할 수 있습니다.

#include<bits/stdc++.h>

using namespace std;

vector<int> manacher(string &S){
  vector<int> R(S.size());
  int i = 0, j = 0;
  while (i < S.size()) {
    while (i-j >= 0 && i+j < S.size() && S[i-j] == S[i+j]) ++j;
    R[i] = j;
    int k = 1;
    while (i-k >= 0 && k+R[i-k] < j) R[i+k] = R[i-k], ++k;
    i += k; j -= k;
  }
  return R;
}

int main(){
  string s;
  cin >> s;
  string ms="$";
  for(auto &nx : s){
    ms.push_back(nx);
    ms.push_back('$');
  }
  auto r=manacher(ms);
  int k=0;
  for(int i=s.size();;i++){
    if(r[i]>=s.size()-k){break;}
    k++;
  }

  cout << s;
  for(int i=0;i<k;i++){
    cout << s[k-1-i];
  }cout << "\n";
  return 0;
}

G

더보기

문제 요약

문제:

$N$개의 정점과 $M$개의 간선을 가진 단순 무방향 그래프 $G$가 주어집니다. 정점은 $1$부터 $N$까지, 간선은 $1$부터 $M$까지의 번호를 가지며, $i$번째 간선은 정점 $U_i$와 $V_i$를 연결합니다. 처음 주어진 그래프 $G$는 홀수 사이클을 포함하지 않으므로 이분 그래프입니다.

게임 규칙:

아오키와 다카하시는 그래프 $G$를 사용하여 게임을 진행합니다. 아오키가 먼저 시작하며, 두 플레이어는 번갈아 가며 다음의 연산을 수행합니다.

  • 정수 쌍 $(i,j)$ ($1 \le i < j \le N$)를 선택합니다. 이때 다음 두 조건을 모두 만족해야 합니다.
    1. 현재 $G$에는 정점 $i$와 $j$를 잇는 간선이 존재하지 않아야 합니다.
    2. 정점 $i$와 $j$를 잇는 간선을 추가해도 홀수 사이클이 생성되지 않아야 합니다.
  • 조건을 만족하는 $(i,j)$를 선택한 후, 정점 $i$와 $j$를 잇는 간선을 $G$에 추가합니다.

연산을 수행할 수 없는 플레이어가 패배하며, 상대 플레이어가 승리합니다.

문제 목표:

양 플레이어가 최적의 전략으로 플레이할 때, 승리하는 플레이어가 누구인지를 결정하는 것입니다.

제약 조건:

  • $1 \le N \le 2 \times 10^5$
  • $0 \le M \le 2 \times 10^5$
  • $1 \le U_i < V_i \le N$
  • 주어진 그래프 $G$는 홀수 사이클을 포함하지 않으며, 다중 간선은 존재하지 않습니다.
  • 모든 입력 값은 정수입니다.

주어진 그래프 $G$는 홀수 사이클이 없으므로, 각 연결 요소는 이분 그래프입니다. 이를 바탕으로 다음 다섯 가지 특성 값을 정의합니다.

  • $x$: 연결성을 유지하면서 이분성을 보존할 수 있도록 추가 가능한 간선의 수
  • $ee$: 양쪽 파트의 정점 수가 모두 짝수인 연결 요소의 수
  • $oo$: 양쪽 파트의 정점 수가 모두 홀수인 연결 요소의 수
  • $eo$: 두 개 이상의 정점을 가진 연결 요소 중 한 파트는 짝수, 다른 파트는 홀수인 경우의 수
  • $iso$: 정점이 $1$개인 연결 요소의 수

이제 승자는 다음과 같이 결정됩니다.

  1. $N$이 홀수인 경우:
    • 첫 번째 플레이어는 $oo+x$가 홀수일 때 승리하고, 짝수이면 두 번째 플레이어가 승리합니다.
  2. $N$이 짝수이고 $eo=0$인 경우:
    • 이 경우 $iso$는 반드시 짝수입니다.
    • 첫 번째 플레이어는 $\frac{iso}{2}+x$가 홀수일 때 승리하고, 짝수이면 두 번째 플레이어가 승리합니다.
  3. $N$이 짝수이고 $1\le eo\le 2$인 경우:
    • 첫 번째 플레이어가 항상 승리합니다.
  4. $N$이 짝수이고 $eo\ge3$인 경우:
    • 첫 번째 플레이어는 $oo+x$가 홀수일 때 승리하고, 짝수이면 두 번째 플레이어가 승리합니다.

증명의 아이디어

  • 게임에서 각 이동은 간선의 수를 $1$씩 증가시키므로, 최종 그래프의 간선 수의 패리티(홀수/짝수)가 중요합니다.
  • $N$이 홀수인 경우:
    최종 이분 그래프에서 한 파트의 정점 수는 홀수, 다른 파트는 짝수가 되어 최종 간선 수는 항상 짝수가 됩니다. 따라서, 승패는 현재 그래프의 간선 수의 패리티, 즉 $oo+x$의 홀짝성에 따라 결정됩니다.
  • $N$이 짝수인 경우:
    최종 간선 수의 패리티는 두 파트의 정점 수가 모두 홀수인지 혹은 모두 짝수인지에 따라 달라집니다.
    특히, $eo$와 $iso$로 대표되는 연결 요소들의 병합 과정에 따라 최종 패리티가 결정되므로, 이 과정을 적절히 처리하는 것이 승패를 좌우합니다.

이와 같이, 위의 다섯 가지 특성 값을 활용하여 게임의 승리 조건을 분석하면, 각 경우에 대해 최적의 전략을 구분할 수 있습니다.

'대회반' 카테고리의 다른 글

앳코더 397  (0) 2025.03.19
코포 995  (1) 2025.03.17
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/05   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함