게시판의 글 하나에 댓글 n개가 달려 있고, 쓴 차례대로 1번부터 n번까지 번호가 붙어 있다.
댓글은 글에 바로 달거나, 먼저 쓴 다른 댓글에 답글로 단다.
i번 댓글에 대해 pᵢ가 주어진다. pᵢ가 0이면 글에 바로 단 댓글이고, 아니면 pᵢ번 댓글에 단 답글이다. 답글은 언제나 번호가 더 작은 댓글에 단다.
화면에는 댓글을 한 줄에 하나씩 다음 차례로 보인다.
글에 바로 단 댓글을 번호가 작은 것부터 보인다.
댓글 하나를 보인 다음에는, 그 댓글에 달린 답글을 번호가 작은 것부터 바로 밑에 보인다.
답글 하나를 보일 때도 같은 규칙으로 그 답글에 달린 답글까지 모두 보인 다음에 다음 답글로 넘어간다.
댓글마다 접혔는지가 주어진다. cᵢ가 1이면 i번 댓글은 접힌 댓글이다.
접힌 댓글은 자기 한 줄은 보이지만, 그 댓글에 달린 답글과 그 답글에 달린 답글들은 하나도 보이지 않는다.
보이는 댓글에는 화면 위에서부터 1, 2, 3, …으로 줄 번호가 붙는다. 보이지 않는 댓글은 줄을 차지하지 않는다.
첫 줄에 화면에 보이는 댓글의 수를 출력하라.
둘째 줄에 1번부터 n번 댓글까지 줄 번호를 차례로 빈칸 하나로 띄워 출력하라. 보이지 않는 댓글은 0으로 출력한다.
입력
첫 줄에 n, 둘째 줄에 p₁ … pₙ, 셋째 줄에 c₁ … cₙ이 빈칸 하나로 나뉘어 주어진다. 1 ≤ n ≤ 10,000, 0 ≤ pᵢ < i, cᵢ는 0 또는 1. 모두 정수다
출력
첫 줄에 보이는 댓글의 수, 둘째 줄에 댓글마다 줄 번호 n개. 보이지 않으면 0
예시 1 · stdin
7
0 1 1 0 2 4 2
0 0 0 1 1 0 0
stdout
6
1 2 5 6 3 0 4
예시 2 · stdin
5
0 1 2 3 0
0 1 0 0 0
stdout
3
1 2 0 0 3
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이
타래 화면은 깊이 우선 탐색(DFS)의 방문 차례 그대로예요: 댓글 하나를 찍고, 그 답글들을 끝까지 내려갔다가 돌아와 다음 댓글로 가요. 첫 예시의 화면은 1, 2, 5, 7, 3, 4 차례이고, 4번이 접혀서 6번은 안 보여요(5번은 접혀 있어도 답글이 없으니 달라지는 것이 없어요). 재귀로 쓰면 가장 짧지만 답글이 한 줄로 10,000번 이어질 수 있어요. Python은 재귀 한도가 기본 1,000이라 RecursionError로 멈추고, 크레이콘도 몇천 단계에서 멈춰요. 그래서 여덟 언어 모두 스택을 직접 써요: 꺼낸 댓글에 줄 번호를 주고, 접히지 않았으면 답글들을 스택에 넣어요. 함정 하나: 스택은 나중에 넣은 것이 먼저 나오니 답글을 번호가 큰 것부터 넣어야 작은 번호가 먼저 나와요. 번호 순으로 넣으면 형제끼리 거꾸로 보여요. 함정 둘: 스택 대신 큐(앞에서 꺼내기)를 쓰면 층별로 도는 너비 우선 탐색이 되어, 답글이 원래 댓글 바로 밑이 아니라 한참 뒤에 나와요. 함정 셋: 접힌 댓글도 자기 줄은 보여요. 내려가지만 않아요. 함정 넷: 접힌 댓글에 바로 달린 답글만 숨기면 틀려요 — 답글의 답글도 안 보여요(둘째 예시의 4번). 내려가지 않으면 저절로 그렇게 돼요. 스택은 언어마다 달라요: Python 리스트의 append·pop, JavaScript·TypeScript 배열의 push·pop, Java ArrayDeque의 push·pop, C# Stack<int>, C++ std::stack(꺼낼 때 top()으로 읽고 pop()으로 뺀다), C는 댓글마다 한 번만 들어가니 n + 1칸 배열과 꼭대기 번호예요. 크레이콘은 목록에 넣기로 넣고 stack[stack.개수 - 1]을 읽은 다음 자리빼기(stack.개수 - 1)로 빼요. C 풀이는 답글을 이음 목록의 맨 앞에 끼우기 때문에 따라가면 큰 번호부터 나와서, 그대로 스택에 넣으면 돼요.
Pythonmain.py · 27 줄
import sysdata = sys.stdin.read().split()n = int(data[0])parent = [0] + [int(x) for x in data[1:1 + n]] # parent[i] = i가 답한 댓글(0이면 글에 바로 단 댓글)folded = [0] + [int(x) for x in data[1 + n:1 + 2 * n]] # folded[i] = 1이면 접힌 댓글# replies[v] = v에 달린 답글들. 답글은 늘 번호가 더 크니 i를 차례로 넣으면 번호 순이 된다# replies[0]은 글에 바로 단 댓글들이다replies = [[] for _ in range(n + 1)]for i in range(1, n + 1): replies[parent[i]].append(i)# 깊이 우선 탐색(DFS)을 스택으로: 꺼낸 차례가 곧 화면의 줄 차례다# 스택은 나중에 넣은 것이 먼저 나오니 번호가 큰 것부터 넣어야 작은 번호가 먼저 나온다line = [0] * (n + 1)shown = 0stack = replies[0][::-1]while stack: v = stack.pop() shown += 1 line[v] = shown if not folded[v]: # 접힌 댓글은 자기 줄만 보이고 답글로는 내려가지 않는다 stack.extend(reversed(replies[v]))print(shown)print(" ".join(map(str, line[1:])))
JavaScriptmain.js · 22 줄
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const n = data[0];// replies[v] = v에 달린 답글들. 답글은 늘 번호가 더 크니 i를 차례로 넣으면 번호 순이 된다// replies[0]은 글에 바로 단 댓글들이다const replies = Array.from({ length: n + 1 }, () => []);for (let i = 1; i <= n; i++) replies[data[i]].push(i);const folded = (v) => data[n + v] === 1; // 접힌 댓글인가// 깊이 우선 탐색(DFS)을 스택으로: 꺼낸 차례가 곧 화면의 줄 차례다// 스택은 나중에 넣은 것이 먼저 나오니 번호가 큰 것부터 넣어야 작은 번호가 먼저 나온다const line = new Array(n + 1).fill(0);let shown = 0;const stack = [...replies[0]].reverse();while (stack.length > 0) { const v = stack.pop(); line[v] = ++shown; if (folded(v)) continue; // 접힌 댓글은 자기 줄만 보이고 답글로는 내려가지 않는다 for (let k = replies[v].length - 1; k >= 0; k--) stack.push(replies[v][k]);}console.log(shown);console.log(line.slice(1).join(" "));
TypeScriptmain.ts · 24 줄
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const n = data[0];// replies[v] = v에 달린 답글들. 답글은 늘 번호가 더 크니 i를 차례로 넣으면 번호 순이 된다// replies[0]은 글에 바로 단 댓글들이다const replies: number[][] = Array.from({ length: n + 1 }, (): number[] => []);for (let i = 1; i <= n; i++) replies[data[i]].push(i);const folded = (v: number): boolean => data[n + v] === 1; // 접힌 댓글인가// 깊이 우선 탐색(DFS)을 스택으로: 꺼낸 차례가 곧 화면의 줄 차례다// 스택은 나중에 넣은 것이 먼저 나오니 번호가 큰 것부터 넣어야 작은 번호가 먼저 나온다const line: number[] = new Array<number>(n + 1).fill(0);let shown = 0;const stack: number[] = [...replies[0]].reverse();while (stack.length > 0) { const v = stack.pop() as number; line[v] = ++shown; if (folded(v)) continue; // 접힌 댓글은 자기 줄만 보이고 답글로는 내려가지 않는다 for (let k = replies[v].length - 1; k >= 0; k--) stack.push(replies[v][k]);}console.log(shown);console.log(line.slice(1).join(" "));
JavaMain.java · 42 줄
import java.io.*;import java.util.*;public class Main { public static void main(String[] args) throws IOException { StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); st.nextToken(); int n = (int) st.nval; // replies.get(v) = v에 달린 답글들. 답글은 늘 번호가 더 크니 i를 차례로 넣으면 번호 순이 된다 // replies.get(0)은 글에 바로 단 댓글들이다 List<List<Integer>> replies = new ArrayList<>(); for (int v = 0; v <= n; v++) replies.add(new ArrayList<>()); for (int i = 1; i <= n; i++) { st.nextToken(); replies.get((int) st.nval).add(i); } boolean[] folded = new boolean[n + 1]; for (int i = 1; i <= n; i++) { st.nextToken(); folded[i] = st.nval == 1; } // 깊이 우선 탐색(DFS)을 스택으로: 꺼낸 차례가 곧 화면의 줄 차례다 // 스택은 나중에 넣은 것이 먼저 나오니 번호가 큰 것부터 넣어야 작은 번호가 먼저 나온다 int[] line = new int[n + 1]; int shown = 0; ArrayDeque<Integer> stack = new ArrayDeque<>(); stack.push(0); // 0번은 글 자체: 줄을 차지하지 않고 접히지도 않는다 while (!stack.isEmpty()) { int v = stack.pop(); if (v != 0) line[v] = ++shown; if (folded[v]) continue; // 접힌 댓글은 자기 줄만 보이고 답글로는 내려가지 않는다 List<Integer> r = replies.get(v); for (int k = r.size() - 1; k >= 0; k--) stack.push(r.get(k)); } StringBuilder sb = new StringBuilder(); sb.append(shown).append('\n'); for (int i = 1; i <= n; i++) sb.append(line[i]).append(i < n ? ' ' : '\n'); System.out.print(sb); }}
C#Main.cs · 38 줄
using System;using System.Collections.Generic;using System.Text;class Program{ static void Main() { var data = Console.In.ReadToEnd().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); int n = int.Parse(data[0]); // replies[v] = v에 달린 답글들. 답글은 늘 번호가 더 크니 i를 차례로 넣으면 번호 순이 된다 // replies[0]은 글에 바로 단 댓글들이다 var replies = new List<int>[n + 1]; for (int v = 0; v <= n; v++) replies[v] = new List<int>(); for (int i = 1; i <= n; i++) replies[int.Parse(data[i])].Add(i); var folded = new bool[n + 1]; for (int i = 1; i <= n; i++) folded[i] = data[n + i] == "1"; // 깊이 우선 탐색(DFS)을 스택으로: 꺼낸 차례가 곧 화면의 줄 차례다 // 스택은 나중에 넣은 것이 먼저 나오니 번호가 큰 것부터 넣어야 작은 번호가 먼저 나온다 var line = new int[n + 1]; int shown = 0; var stack = new Stack<int>(); stack.Push(0); // 0번은 글 자체: 줄을 차지하지 않고 접히지도 않는다 while (stack.Count > 0) { int v = stack.Pop(); if (v != 0) line[v] = ++shown; if (folded[v]) continue; // 접힌 댓글은 자기 줄만 보이고 답글로는 내려가지 않는다 for (int k = replies[v].Count - 1; k >= 0; k--) stack.Push(replies[v][k]); } var sb = new StringBuilder(); sb.Append(shown).Append('\n'); for (int i = 1; i <= n; i++) sb.Append(line[i]).Append(i < n ? ' ' : '\n'); Console.Write(sb); }}
C++main.cpp · 39 줄
#include <iostream>#include <stack>#include <vector>using namespace std;int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; // replies[v] = v에 달린 답글들. 답글은 늘 번호가 더 크니 i를 차례로 넣으면 번호 순이 된다 // replies[0]은 글에 바로 단 댓글들이다 vector<vector<int>> replies(n + 1); for (int i = 1; i <= n; i++) { int p; cin >> p; replies[p].push_back(i); } vector<int> folded(n + 1, 0); for (int i = 1; i <= n; i++) cin >> folded[i]; // 깊이 우선 탐색(DFS)을 스택으로: 꺼낸 차례가 곧 화면의 줄 차례다 // 스택은 나중에 넣은 것이 먼저 나오니 번호가 큰 것부터 넣어야 작은 번호가 먼저 나온다 vector<int> line(n + 1, 0); int shown = 0; stack<int> st; st.push(0); // 0번은 글 자체: 줄을 차지하지 않고 접히지도 않는다 while (!st.empty()) { int v = st.top(); st.pop(); if (v != 0) line[v] = ++shown; if (folded[v]) continue; // 접힌 댓글은 자기 줄만 보이고 답글로는 내려가지 않는다 for (auto it = replies[v].rbegin(); it != replies[v].rend(); ++it) st.push(*it); } cout << shown << '\n'; for (int i = 1; i <= n; i++) cout << line[i] << (i < n ? ' ' : '\n'); return 0;}
Cmain.c · 38 줄
#include <stdio.h>#include <stdlib.h>int main(void) { int n; if (scanf("%d", &n) != 1) return 0; // 답글 목록을 배열 둘로: first[v]에서 시작해 next[i]로 따라간다(v = 0은 글 자체) // 새 답글을 목록의 맨 앞에 끼우므로, 따라가면 번호가 큰 답글부터 나온다 int *first = calloc(n + 1, sizeof(int)); int *next = calloc(n + 1, sizeof(int)); int *folded = calloc(n + 1, sizeof(int)); for (int i = 1; i <= n; i++) { int p; scanf("%d", &p); next[i] = first[p]; first[p] = i; } for (int i = 1; i <= n; i++) scanf("%d", &folded[i]); // 깊이 우선 탐색(DFS)을 스택으로: 꺼낸 차례가 곧 화면의 줄 차례다 // 스택은 나중에 넣은 것이 먼저 나오니 번호가 큰 것부터 넣어야 작은 번호가 먼저 나온다 int *line = calloc(n + 1, sizeof(int)); int *stack = malloc((n + 1) * sizeof(int)); // 댓글마다 한 번만 들어간다 int top = 0, shown = 0; stack[top++] = 0; // 0번은 글 자체: 줄을 차지하지 않고 접히지도 않는다 while (top > 0) { int v = stack[--top]; if (v != 0) line[v] = ++shown; if (folded[v]) continue; // 접힌 댓글은 자기 줄만 보이고 답글로는 내려가지 않는다 for (int r = first[v]; r != 0; r = next[r]) stack[top++] = r; } printf("%d\n", shown); for (int i = 1; i <= n; i++) printf(i > 1 ? " %d" : "%d", line[i]); printf("\n"); free(first); free(next); free(folded); free(line); free(stack); return 0;}
Craykonmain.cray · 51 줄
n = 콘솔.수들읽기()[0];parent = 콘솔.수들읽기(); // parent[i - 1] = i번 댓글이 답한 댓글(0이면 글에 바로 단 댓글)folded = 콘솔.수들읽기(); // folded[i - 1] = 1이면 접힌 댓글// 목록은 10,000칸까지라 댓글 번호에서 1을 빼 0번부터 둔다// replies[v] = v에 달린 답글들. 답글은 늘 번호가 더 크니 차례로 넣으면 번호 순이 된다replies = [];반복 (v = 0; v < n; v++){ replies.넣기([]);}tops = []; // 글에 바로 단 댓글들반복 (i = 0; i < n; i++){ 만약 (parent[i] == 0) { tops.넣기(i); } 아니면 { replies[parent[i] - 1].넣기(i); }}// 깊이 우선 탐색(DFS)을 스택으로: 꺼낸 차례가 곧 화면의 줄 차례다// 스택은 나중에 넣은 것이 먼저 나오니 번호가 큰 것부터 넣어야 작은 번호가 먼저 나온다line = 새목록(n, 0);shown = 0;stack = [];반복 (k = tops.개수 - 1; k >= 0; k--){ stack.넣기(tops[k]);}동안 (stack.개수 > 0){ v = stack[stack.개수 - 1]; stack.자리빼기(stack.개수 - 1); shown += 1; line[v] = shown; // 접힌 댓글은 자기 줄만 보이고 답글로는 내려가지 않는다 만약 (folded[v] == 0) { 반복 (k = replies[v].개수 - 1; k >= 0; k--) { stack.넣기(replies[v][k]); } }}콘솔.쓰기(shown);콘솔.쓰기(line);