-
[BOJ] 5820 : 경주
5820 : 경주 풀이 트리에서 분할정복을 하자! linear 배열에서의 분할정복은 중앙을 기준으로 좌우로 쪼개어 내려간다. tree에서의 분할정복도 마찬가지로 중앙을 기준으로 들어가면 된다. 트리에서 중앙은 무엇일까? 트리에 centroid라는 것이 있다. centroid는 어떤 노드를 의미한다. 트리에서 어떤 정점을 제거하면 여러 subtree가 생기게 된다. 이때 생기는 모든 subtree의 크기가 N/2 이하가 되게하는 정점을 의미한다. 다시 말해, centroid는 제거하였을 때 모든 subtree의 크기 <= 노드 수/2가 되도록 하는 정점을 의미한다. 이 문제를 분할정복으로 풀기 전에 아래의 문제를 먼저 분할정복으로...
-
[BOJ] 5820 : 경주
5820 : 경주 풀이 트리에서 분할정복을 하자! linear 배열에서의 분할정복은 중앙을 기준으로 좌우로 쪼개어 내려간다. tree에서의 분할정복도 마찬가지로 중앙을 기준으로 들어가면 된다. 트리에서 중앙은 무엇일까? 트리에 centroid라는 것이 있다. centroid는 어떤 노드를 의미한다. 트리에서 어떤 정점을 제거하면 여러 subtree가 생기게 된다. 이때 생기는 모든 subtree의 크기가 N/2 이하가 되게하는 정점을 의미한다. 다시 말해, centroid는 제거하였을 때 모든 subtree의 크기 <= 노드 수/2가 되도록 하는 정점을 의미한다. 이 문제를 분할정복으로 풀기 전에 아래의 문제를 먼저 분할정복으로...
-
[BOJ] 15368 : Birokracija
15368 : Birokracija 풀이 트리에 존재하는 모든 노드에 대해, 각각의 노드에서 루트까지의 거리를 더하는 문제이다. 음… 그냥 짜면 된다. 코드 #include <cstdio> int n, par[200002], cnt[200002]; long long ans[200002]; int main() { scanf("%d", &n); for (int i = 2; i <= n; i++) scanf("%d", &par[i]); for (int i = n; i >= 1; i--) { ans[par[i]] += ++ans[i] + ++cnt[i]; cnt[par[i]] += cnt[i]; } for (int i = 1; i <= n; i++) printf("%lld ",...
-
[BOJ] 4256 : 트리
4256 : 트리 풀이 어찌저찌 트리 다시 만들고 잘 짜면 된다. 풀이가 너무 부실한데? 다음의 예제를 보자. 8 3 6 5 4 8 7 1 2 5 6 8 4 3 1 2 7 예를 들어, 지금 inorder로 3을 보고 있다면 preorder 기준으로 3을 반으로 나누면 5 6 8 4, 1 2 7이 있다. 이 말은 노드3에 대해, 5 6 8 4는 왼쪽, 1 2 7은 오른쪽 서브트리 노드들이라는 뜻이다. 이제 이를 토대로 쭉 짜보자....
-
[BOJ] 15267 : Justified Jungle
15267 : Justified Jungle 풀이 트리를 일단 나눴다면, 나눠진 후의 모든 컴포넌트에 속한 노드 수는 같아야 한다. 즉, 노드는 n의 약수로만 나눠질 수 있다. 100만 이하 자연수의 약수 개수가 최대 240개니까 O(240*n) 잘 커팅하면 6초 안에 될 거 같은데 안 됨 흠… 아무튼 나이브하게는 안 돌아가므로 조금 더 생각을 해야한다. 트리를 돌며 gcd(서브트리의 노드 개수, n)의 개수를 세어주자. 음… 그냥 코드를 보는 게 더 빠르겠다. 코드 #include <cstdio> #include <vector> using namespace std; int n,...
-
[BOJ] 1068 : 트리
1068 : 트리 풀이 잘 짜주자. 코드 #include <stdio.h> int n, i, now, del, cnt, gph[51], idg[51]; int main() { scanf("%d", &n); for (i = 0; i < n; i++) { scanf("%d", &gph[i]); if (~gph[i]) idg[gph[i]]++; } scanf("%d", &del); if (gph[del] == -1) return !printf("0"); idg[gph[del]]--; for (i = 0; i < n; i++) { if (idg[i]) continue; cnt++; for (now = i; ~now; now = gph[now]) if (now == del) { cnt--; break; }...
-
[BOJ] 9489 : 사촌
9489 : 사촌 풀이 부모를 찾으러 산으로 갈까요~ 사촌을 찾으러 강으로 갈까요~ 코드 #include <cstdio> int main() { while (1) { int n, k, a[1111] = { -1 }, p[1111] = { -1 }; scanf("%d %d", &n, &k); if (!n && !k) break; int cnt = -1, idx = 0; for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); if (a[i] == k) idx = i; if (a[i] != a[i - 1]...
-
[BOJ] 1991 : 트리 순회
1991 : 트리 순회 풀이 아 퇴사하고싶다 코드 #include <stdio.h> #define p(c) putchar(c); int n, i; char a, b, c, t[99][2]; void f(int x) { if (i == 0) p(x); if (t[x][0] != '.') f(t[x][0]); if (i == 1) p(x); if (t[x][1] != '.') f(t[x][1]); if (i == 2) p(x); } int main() { scanf("%d", &n); for (i = 0; i < n; i++) { scanf(" %c %c %c", &a, &b, &c); t[a][0] = b,...
-
[BOJ] 11725 : 트리의 부모 찾기
11725 : 트리의 부모 찾기 풀이 별 하나에 추억과 별 하나에 사랑과 별 하나에 쓸쓸함과 별 하나에 동경(憧憬)과 별 하나에 시와 별 하나에 어머니, 어머니 코드 #include <stdio.h> #include <vector> using namespace std; int n, pnt[100001]; vector<int> gph[100001]; void dfs(int now) { for (int nxt : gph[now]) if (pnt[now] ^ nxt) pnt[nxt] = now, dfs(nxt); } int main() { scanf("%d", &n); for (int i = 0, u, v; i < n - 1; i++) {...
-
[BOJ] 1068 : 트리
1068 : 트리 풀이 리프 노드라는 것은, 들어오는 간선이 없다는 것. 코드 #include <stdio.h> int n, i, now, del, cnt, gph[51], idg[51]; int main() { scanf("%d", &n); for (i = 0; i < n; i++) { scanf("%d", &gph[i]); if (~gph[i]) idg[gph[i]]++; } scanf("%d", &del); if (gph[del] == -1) return ~printf("0"); idg[gph[del]]--; for (i = 0; i < n; i++) { if (idg[i]) continue; cnt++; for (now = i; ~now; now = gph[now]) if (now ==...
-
[BOJ] 1967 : 트리의 지름
1967 : 트리의 지름 풀이 트리에서 임의의 노드 ㄱ을 고른 뒤 ㄱ에서 가장 먼 노드 ㄴ을 찾고 ㄴ에서 가장 먼 노드 ㄷ을 찾으면 ㄴ과 ㄷ이 트리의 지름이 된다 (!) 코드 #include <cstdio> #include <vector> #include <algorithm> using namespace std; const int n_ = 1e4 + 4; struct edg { int idx, dst; }; int n, abc, sum; vector<edg> gph[n_]; void dfs(int prv, int now, int dst) { if (sum < dst) sum = dst, abc...
-
[BOJ] 1693 : 트리 색칠하기
1693 : 트리 색칠하기 풀이 http://codersbrunch.blogspot.kr/2017/07/1693.html 읽고 구현한 코드입니다. 코드 #include <stdio.h> #include <vector> #include <algorithm> using namespace std; const int n_ = 1e5 + 5; int n, dp[n_][18]; vector<int> gph[n_]; void dfs(int prv, int now) { dp[now][0] = 2e9; for (int col = 1; col <= 17; col++) dp[now][col] += col; for (int nxt : gph[now]) { if (nxt == prv) continue; dfs(now, nxt); int fst = 0, snd = 0; for (int col...
-
[BOJ] 14438 : 수열과 쿼리 17
14438 : 수열과 쿼리 17 풀이 sqrt decomposition을 이용해서 풀었다. 물론 segment tree를 이용하면 훨씬 더 빠르다. 코드 #include <stdio.h> #include <math.h> #include <algorithm> using namespace std; int n, m, sqr, a[100001], bkt[100001]; void upd(int i, int v) { a[i] = v, bkt[i / sqr] = 1e9 + 1e8; int tmp = i / sqr; for (int j = tmp * sqr; j < (tmp + 1) * sqr; j++) bkt[tmp] = min(bkt[tmp], a[j]); }...
-
[BOJ] 14570 : 나무 위의 구슬
14570 : 나무 위의 구슬 풀이 문제의 조건을 그대로 가져다 써보자. 모든 노드에 대해, 왼쪽 구슬의 수가 오른쪽 구슬의 수보다 크거나 같다. n개의 구슬 중 n/2+n%2개는 왼쪽, n/2개는 오른쪽으로 떨어지게 된다. 종단 노드를 만날 때 까지 돌려보자~~ 코드 #include <stdio.h> struct hello { int l, r; } t[200001]; int main() { int n; long long k; scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d %d", &t[i].l, &t[i].r); scanf("%lld", &k); int cur...
-
[BOJ] 11438 : LCA2
11438 : LCA2 풀이 이번엔 naive가 아닌 풀이! 이전의 naive한 풀이에서, 우리는 최악의 경우에 n번의 노드를 하나씩하나씩 올라가야했다. 하지만 이를 log(n)으로 줄일 수 있다! pnt[i][j] = i번 노드의 2^j번째 조상으로 두고 풀면 된다. 트리의 깊이를 구할 때 2^j번째 조상을 구해두자. tmp = anc[i][j-1]일 때, anc[i][j]는 anc[tmp][j-1]과 같다. 즉, i의 2^j번째 조상은 2^(j-1)번째 조상의 2^(j-1)번째 조상과 같다는 의미이다. i의 2^3=8번째 조상은 2^2=4번째 조상의 2^2=4번째 조상과 같다. naive한 코드에서 2^j를 도는 동작만 추가해주면 어렵지 않게(?) 훨씬 빠르게(!)...
-
[BOJ] 11437 : LCA
11437 : LCA 풀이 naive한 풀이! dfs 먼저 돌려서 각 노드마다 깊이를 구해준 뒤, u, v 두 노드 중 더 큰 노드의 깊이를 끌어 올려준 다음에 두 노드에서 동시에 하나씩 올라가며 같은 노드에서 마주칠 때 까지 올려주면 된다. 코드 #include <stdio.h> #include <algorithm> #include <vector> using namespace std; const int n_ = 5e4 + 3; int n, m; int dph[n_], pnt[n_]; vector<int> gph[n_]; inline void dfs(int now, int cnt) { dph[now] = cnt++; for (auto...