Skip to main content Link Menu Expand (external link) Document Search Copy Copied

1248. 공통조상

Code Battle (`23 동계 대학생 S/W 알고리즘 특강 기초학습 문제)


본 게시글은 개인 학습 용도로 작성한 게시글입니다.

(문제 출처: SW Expert Academy)


  • 이진 트리에서 임의의 두 정점의 가장 가까운 공통 조상 찾기 + 서브 트리 크기 구하기.
  • $V$개의 정점과 $E$개의 간선이 주어진다.
  • 공통 조상을 찾을 두 노드가 주어질 것이므로, 그들의 조상을 거슬러 올라가며 비교할 것이다. 따라서 각 노드에는 그들의 부모 정보가 있어야 한다. 비교에는 $O(log{V})$ 시간이 걸린다. (최악의 경우 $O(V)$)
  • 어떤 노드를 루트로 하는 서브트리의 크기를 구해야 한다. 각 노드는 그 들의 자식에 대한 정보를 담지 않을 것이므로 트리 순회를 통해 직접 크기를 구하는 것은 불가능하다. 대안으로, 각 노드에 서브 트리 크기를 기록해 두고, 자식노드와 부모노드를 연결 할 때 부모의 서브트리 크기를 갱신해주는 방법을 사용하여 풀이한다. 갱신하는 데에는 $O(Elog{V})$ 시간이 걸린다. (최악의 경우, $O(EV)$)
  • 매 테스트 케이스마다 각 노드별 부모와 서브트리 크기를 초기화하는데 $O(V)$의 시간이 걸린다.
  • 종합하면 각 테스트 케이스 별 시간 복잡도는 $O(V+Elog{V})$ ~ $O(EV)$

#include <iostream>

#define MAX_V 10000

using namespace std;

struct Node {
	int subtreeSize;
	Node* parent;
};

Node nodes[MAX_V+1];

// O(log(V)) ... or O(V) in worst case.
static Node* findMostCommonAncestor(Node* nodeX, Node* nodeY) {
	int depthX = 0;
	int depthY = 0;
	for (Node* curX = nodeX; curX != nullptr; curX = curX->parent) {
		depthX++;
	}
	for (Node* curY = nodeY; curY != nullptr; curY = curY->parent) {
		depthY++;
	}
	while (depthX > depthY) {
		nodeX = nodeX->parent;
		depthX--;
	}
	while (depthX < depthY) {
		nodeY = nodeY->parent;
		depthY--;
	}
	while (nodeX != nullptr && nodeY != nullptr) {
		if (nodeX == nodeY) {
			return nodeX;
		}
		nodeX = nodeX->parent;
		nodeY = nodeY->parent;
	}
	return nullptr;
}

int getNumOf(Node* node) {
	return (int) ((size_t) node - (size_t) nodes) / sizeof(Node);
}

static void solve(int test_case_no)
{
	int V, E, x, y, parent, child;
	Node *currentNode, *parentNode;
	cin >> V >> E >> x >> y;
	// O(V)에 트리 초기화
	for (int v = 1; v <= V; v++) {
		currentNode = nodes + v;
		currentNode->subtreeSize = 1;
		currentNode->parent = nullptr;
	}
	// O(ElogV)에 트리 구축
	for (int i = 0; i < E; i++) {
		cin >> parent >> child;
		currentNode = nodes + child;
		parentNode = nodes + parent;
		currentNode->parent = parentNode;
		while (parentNode != nullptr) {
			parentNode->subtreeSize += currentNode->subtreeSize;
			parentNode = parentNode->parent;
		}
	}
	Node *mca = findMostCommonAncestor(nodes+x, nodes+y);
	printf("#%d %d %d\n", test_case_no, getNumOf(mca), mca->subtreeSize);
}

int main(int argc, char **argv)
{
	int T;
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	cin >> T;
	for (int test_case_no = 1; test_case_no <= T; ++test_case_no)
	{
		solve(test_case_no);
	}
	return 0;
}

Back to top