DOJ logoDOJ
문제
최근 추가된 문제들저장한 문제
카테고리DLC대회그룹팀게시판
랭킹
문제 풀이 랭킹스트릭 랭킹DOJ Power 랭킹
현황
문제 제안로그인회원가입
DOJ logoDOJ

OI 서브태스크 채점, Elo 레이팅, Discord 인증을 갖춘 커뮤니티 온라인 저지.

문제
문제 목록카테고리
커뮤니티
대회랭킹게시판공지사항외부 대회 개최이용 안내
계정
로그인회원가입채점 현황
정책
이용약관DLC 이용 규칙개인정보 처리방침API 사용 설명서언어별 채점 환경대회 순위 보상레이팅과 퍼포먼스
© 2026 DOJ
모든 권리 보유
해설

겉날개

문제로 돌아가기

해설

실제 이동 시간에 222를 곱한 값을 비용으로 사용하자. 길이가 www인 간선을 가속 상태로 지나면 비용은 www, 일반 상태로 지나면 비용은 2w2w2w이다.

[서브태스크 1] N≤10N\leq10N≤10 (7점)

이 서브태스크에서는 산책 자체를 최단 경로 문제로 바꿀 수 있다.

시작점 sss를 고정하고 상태를 (M,p,v,t)(M,p,v,t)(M,p,v,t)로 둔다.

  • MMM: 지금까지 한 번 이상 지난 간선들의 집합
  • vvv: 현재 정점
  • ppp: 바로 전에 있던 정점. 시작 상태에서는 별도의 값으로 둔다.
  • ttt: 현재 가속 상태인지 여부

현재 정점 vvv에서 이웃 uuu로 이동한다고 하자. u=pu=pu=p이면 방금 지나온 간선으로 즉시 돌아가는 U턴이다. 이 경우 vvv에 폭죽 발판이 없으면 일반 상태로 출발하고, 발판이 있으면 속도를 잃은 직후 폭죽을 사용하므로 가속 상태로 출발한다. u≠pu\neq pu이면 출발 상태는 현재 상태와 같다.

간선을 지난 뒤 uuu에 폭죽 발판이 있으면 다음 상태는 가속 상태가 된다. 이동 비용은 출발 상태가 가속이면 www, 일반이면 2w2w2w이다.

초기 상태는 (0,⊥,s,A[s])(0,\bot,s,A[s])(0,⊥,s,A[s])이다. 모든 간선을 지난 뒤 다시 sss에 도착한 상태까지의 최단 거리를 다익스트라 알고리즘으로 구한다. 모든 시작점 sss를 시도한 값의 최솟값이 답이다.

방문한 간선 집합은 N−1N-1N−1비트로 저장할 수 있다. 상태 수는 O(2NN2)O(2^N N^2)O(2NN2)이다.

[서브태스크 2] N≤15N\leq15N≤15 (13점)

앞 풀이에서 시작 상태를 제외하면 ppp와 vvv는 항상 인접하다. 따라서 두 정점 번호를 따로 저장할 필요가 없다.

각 방향 간선 p→vp\to vp→v에 번호를 붙이고, 상태를 (M,e,t)(M,e,t)(M,e,t)로 둔다. 여기서 eee는 마지막으로 지난 방향 간선이다. 시작 상태만 별도로 처리한다.

트리의 방향 간선은 2(N−1)2(N-1)2(N−1)개이므로 한 간선 집합에 대해 가능한 위치 상태가 O(N)O(N)O(N)개로 줄어든다. 전이는 방향 간선의 끝점에서 다음 방향 간선을 고르는 방식으로 만든다. 다음 방향 간선이 현재 방향 간선의 역방향이면 U턴이고, 아니면 U턴이 아니다.

시작점별 다익스트라 알고리즘의 상태 수는 O(2NN)O(2^N N)O(2NN)이고, 전체 시간은 대략 O(2NN3log⁡(2NN))O(2^N N^3\log(2^N N))O(2NN이다.

[서브태스크 3] N≤50N\leq50N≤50 (34점)

지수적인 탐색 대신 트리의 부분 산책을 합치는 DP를 만든다. 이 단계에서는 뒤에서 사용할 일정한 증가 성질은 필요하지 않다.

시작점 정리

폭죽 발판이 하나도 없다면 상태는 항상 일반 상태이다. 시작점으로 돌아오는 산책은 각 간선을 양방향으로 적어도 한 번씩 지나야 하므로 답은 4∑ewe4\sum_e w_e4∑e​we​이다.

폭죽 발판이 하나라도 있다면 시작점은 폭죽 정점으로 잡을 수 있다. 최적 산책에서 처음 폭죽 정점에 도착한 시점부터 이동 순서를 순환시키면, 새 산책은 가속 상태로 시작하고 비용은 증가하지 않는다.

폭죽 정점 xxx를 인접 간선마다 서로 다른 복사본으로 나눈다. 각 복사본은 해당 간선 하나에만 연결된 폭죽 리프이다. 원래 xxx에 도착할 때마다 가속 상태로 초기화되므로, 서로 다른 인접 간선 쪽 산책은 독립적으로 분리한 뒤 다시 이어 붙일 수 있다. 따라서 분리해서 얻은 트리들의 답을 각각 구해 더하면 된다.

이하 분리해서 얻은 트리 하나만 생각한다. 이 트리에서는 모든 폭죽 정점이 리프이다.

red와 blue

가속 상태의 이동을 red, 일반 상태의 이동을 blue라고 부르자.

폭죽이 없는 내부 정점에서 들어온 간선으로 즉시 돌아가는 이동은 제거할 수 있다. 그 이동은 다른 간선의 피복에 도움을 주지 않고, 이후 상태만 일반 상태로 만들 수 있기 때문이다.

따라서 내부 정점에서는 이동 색이 유지된다. 색이 바뀌는 곳은 리프뿐이다.

  • 폭죽 리프에서는 blue로 들어와도 red로 나갈 수 있다.
  • 일반 리프에서 U턴하면 red가 blue로 바뀐다.

balance

트리를 임의의 내부 정점에서 루팅한다. 정점 vvv의 서브트리에서 부모 방향으로 더 이어져야 하는 red 경로의 순증가량을 balance라고 하자.

balance가 +k+k+k이면 폭죽 리프에서 시작한 red 경로 kkk개가 부모 쪽으로 더 나가야 한다. balance가 −k-k−k이면 일반 리프에서 끝나야 하는 red 경로 kkk개를 부모 쪽에서 더 받아야 한다.

balance가 000이 아니면 경계에서 필요한 구조가 하나로 정해진다. balance가 000일 때만 다음 다섯 경우를 구분한다.

  • ZEZ_EZE​: 아직 아무 구조도 없다.
  • ZRZ_RZR​: 부모 경계에 red만 드러난다.
  • ZBZ_BZ: 부모 경계에 blue만 드러난다.

Pv[k]P_v[k]Pv​[k]를 balance +k+k+k인 최소 비용, Nv[k]N_v[k]Nv​를 balance 인 최소 비용으로 둔다.

폭죽 리프에서는 ZR=0Z_R=0ZR​=0이고 P[k]=0P[k]=0P[k]=0이다. 일반 리프에서는 ZB=0Z_B=0Z이고 이다. 내부 정점은 자식을 합치기 전에 인 상태에서 시작한다.

분리된 트리 전체의 리프 수를 LLL이라 하자. 루트에서는 balance가 000이어야 하므로 ∣k∣≤L|k|\leq L∣k∣≤L인 상태만 저장한다. 어떤 경계에서 ∣k∣>L|k|>L∣k∣>L이라면 같은 종류의 리프에서 나온 경로가 중복되고, 반대쪽에도 이를 상쇄하기 위한 중복 경로가 존재한다. 양쪽에서 하나씩 제거해도 간선 피복은 유지되며 비용은 증가하지 않는다. 이 과정을 반복하면 인 최적해를 얻을 수 있다.

부모 간선 붙이기

길이가 www인 부모 간선을 붙인다고 하자.

balance의 절댓값이 kkk이면 그 간선을 red로 kkk번, blue로 kkk번 지나므로 3wk3wk3wk를 더한다. ZRZ_RZR에는 red 왕복 비용 , 에는 blue 왕복 비용 , 와 에는 두 색의 왕복 비용 를 더한다.

두 부분 합치기

두 nonzero balance를 합치면 정수처럼 더한다. 양수와 음수가 정확히 상쇄되면 red와 blue가 이어지므로 ZCZ_CZC​가 된다.

balance가 000인 상태끼리는 다음 규칙을 사용한다.

  • ZEZ_EZE​는 항등원이다.
  • ZRZ_RZR​끼리는 ZRZ_RZ, 끼리는 이다.

두 부분의 모든 상태쌍을 확인하여 결과 상태를 갱신한다. 한 병합에 O(L2)O(L^2)O(L2)이 들고, 전체 시간은 O(NL2)O(NL^2)O(NL2), 메모리는 O(NL)O(NL)O(N이다.

[서브태스크 4] N≤100N\leq100N≤100 (51점)

서브태스크 3의 DP에서 각 정점마다 길이 2L+52L+52L+5인 배열을 전부 초기화하면 불필요한 작업이 많다. 현재까지 합친 자식들이 만들 수 있는 balance 구간만 유지한다.

자식 하나를 새로 합칠 때 기존 구간과 자식 구간의 상태쌍만 순회한다. 다섯 개의 ZZZ 상태는 별도로 상수 시간에 처리한다. 또한 비용이 무한대인 상태는 반복문에서 건너뛴다.

상태 정의와 전이는 바뀌지 않는다. 시간 복잡도는 여전히 O(NL2)O(NL^2)O(NL2)이지만, 실제 순회 범위는 각 부분에서 도달 가능한 balance 구간으로 제한된다.

[서브태스크 5] N≤400N\leq400N≤400 (72점)

같은 DP를 사용하되, 병합 순서를 정리한다.

각 정점의 자식들을 서브트리 리프 수가 작은 순서로 합친다. 두 배열을 합칠 때는 더 짧은 배열을 바깥 반복문에 둔다. 각 정점의 DP는 자식 처리가 끝난 뒤 부모에게 넘기고, 더 이상 사용하지 않는 자식 배열은 즉시 버린다.

한 병합에서 확인하는 상태쌍 수는 두 부분의 도달 가능한 balance 개수의 곱이다. 전체 시간은 O(NL2)O(NL^2)O(NL2), 메모리는 O(NL)O(NL)O(NL)이다. L≤NL\leq NL≤N이므로 각각 과 이다.

[서브태스크 6] N≤2 000N\leq2\,000N≤2000 (100점)

O(N3)O(N^3)O(N3) 풀이의 병목은 긴 balance 배열의 뒤쪽까지 모두 저장하고 합치는 것이다. 배열의 뒤쪽은 일정한 차이로 증가한다.

정점 vvv의 서브트리에 있는 폭죽 리프 수를 BvB_vBv​, 일반 리프 수를 DvD_vDv​라 하자. 또한 sv+s_v^+를 에 에서 가장 가까운 폭죽 리프까지의 거리를 곱한 값, 를 에 가장 가까운 일반 리프까지의 거리를 곱한 값으로 둔다. 해당 종류의 리프가 없으면 무한대로 둔다.

k≥Bvk\geq B_vk≥Bv​이면 Pv[k+1]−Pv[k]=sv+P_v[k+1]-P_v[k]=s_v^+Pv​[이다. 대칭적으로 이면 이다.

첫 번째 식을 증명하자. k>Bvk>B_vk>Bv​개의 미완성 red 경로가 있으면 같은 폭죽 리프에서 시작한 경로가 적어도 두 개 있다. 그중 하나를 그 리프에서 vvv까지 제거해도 같은 경로가 하나 남으므로 필요한 간선 피복과 연결은 유지된다. 제거되는 비용은 그 리프까지 거리의 333배이며 sv+s_v^+ 이상이다.

반대로 가장 가까운 폭죽 리프에서 vvv까지 경로 하나를 더 만들면 정확히 sv+s_v^+sv+​의 비용으로 balance를 하나 늘릴 수 있다. 두 방향을 합치면 차이가 정확히 sv+s_v^+sv+임을 얻는다. 도 같은 방식으로 증명된다.

따라서 실제로 저장할 값은 Pv[1],…,Pv[Bv]P_v[1],\ldots,P_v[B_v]Pv​[1],…,Pv​[Bv와 뿐이다. 그 뒤의 값은 마지막 저장값과 일정한 증가량으로 계산한다.

PvP_vPv​의 저장 구간은 반드시 BvB_vBv​를 기준으로 해야 한다. 일반 리프 수 DvD_vD를 기준으로 자르면, 아직 사용하지 않은 폭죽 리프를 처음 사용하는 비용과 이미 사용한 가까운 폭죽 리프를 반복하는 비용의 순서를 잘못 판단할 수 있다.

압축된 배열 병합

두 부분의 리프 수를 각각 x,yx,yx,y라 하자.

저장된 구간끼리의 상태쌍은 직접 확인한다. 한쪽만 일정 증가 구간에 있는 경우에는 앞쪽 최솟값이나 뒤쪽 최솟값을 미리 계산하여 한 번의 조회로 처리한다.

부호가 반대인 두 상태를 합칠 때는 저장 구간-저장 구간, 저장 구간-일정 증가 구간, 일정 증가 구간-저장 구간, 일정 증가 구간-일정 증가 구간으로 나눈다. 가운데 두 경우는 뒤쪽 최솟값으로 처리한다. 마지막 경우에는 두 증가량이 음수가 아니므로 조건을 만족하는 가장 작은 인덱스쌍만 확인하면 된다.

한 병합의 시간은 O(xy+x+y)O(xy+x+y)O(xy+x+y)이다.

모든 병합의 xyxyxy 항을 합치자. 두 리프의 순서 없는 쌍 하나는 그 두 리프가 처음 같은 부분에 들어오는 병합에서 한 번만 처리된다. 따라서 모든 곱 항의 합은 O(L2)O(L^2)O(L2)이다. 선형 스캔의 총합은 O(NL)O(NL)O(NL)이고 L≤NL\leq NL이다.

분리된 모든 트리의 정점 수와 리프 수의 합은 O(N)O(N)O(N)이므로 전체 시간 복잡도는 O(N2)O(N^2)O(N2)이다. 모든 정점의 DP를 저장해도 메모리 복잡도는 O(N2)O(N^2)O(N2 이하이다.

루트에서 답 구하기

분리된 트리의 루트에서는 balance가 000이어야 한다. red만 있는 연결된 산책, blue만 있는 연결된 산책, 두 색이 연결된 산책은 모두 가능하므로 답은 min⁡(ZR,ZB,ZC)\min(Z_R,Z_B,Z_C)min(ZR​,ZB​,Z이다.

ZSZ_SZS​는 두 구조가 한 산책으로 연결되지 않은 상태이므로 사용할 수 없다. ZEZ_EZE​는 분리된 트리에 간선이 하나도 없을 때만 비용 000으로 허용한다.

각 분리된 트리의 답을 모두 더하면 원래 트리의 답이 된다.

Solution written by GPT5.6


=
p
3
log
(
2N
N
))
B
​
  • ZCZ_CZC​: red와 blue가 모두 있고 서로 연결되어 있다.
  • ZSZ_SZS​: red와 blue가 모두 있지만 서로 분리되어 있다.
  • [
    k
    ]
    −k-k−k
    B​
    =
    0
    N[k]=0N[k]=0N[k]=0
    ZE=0Z_E=0ZE​=0
    ∣k∣≤L|k|\leq L
    ∣k∣≤L
    ​
    2w2w2w
    ZBZ_BZB​
    4w4w4w
    ZCZ_CZC​
    ZSZ_SZS​
    6w6w6w
    R
    ​
    ZBZ_BZB​
    ZBZ_BZB​
  • ZRZ_RZR​과 ZBZ_BZB​를 합치면 ZSZ_SZS​이다.
  • ZSZ_SZS​에 ZRZ_RZR​, ZBZ_BZB​, ZSZ_SZS​를 합치면 ZSZ_SZS​이다.
  • ZCZ_CZC​를 비어 있지 않은 상태와 합치면 ZCZ_CZC​이다.
  • L
    )
    O(N3)O(N^3)
    O(N3)
    O(N2)O(N^2)O(N2)
    sv+​
    333
    vvv
    sv−s_v^-sv−​
    333
    k
    +
    1]−
    Pv​[k]=
    sv+​
    k≥Dvk\geq D_vk≥Dv​
    Nv[k+1]−Nv[k]=sv−N_v[k+1]-N_v[k]=s_v^-Nv​[k+1]−Nv​[k]=sv−​
    s
    v+​
    ​
    NvN_vNv​
    ​
    ]
    Nv[1],…,Nv[Dv]N_v[1],\ldots,N_v[D_v]Nv​[1],…,Nv​[Dv​]
    v
    ​
    ≤
    N
    )
    C​
    )