본문 바로가기

Problem Solving/CodeUp

[CodeUp 4035] 커피전문점

 

커피전문점

1. 첫째 줄에 트리의 노드(후보지)의 개수를 나타내는 정수 n이 주어진다. (2≤n≤100,000) 후보지는 1부터 n까지의 번호로 구분된다. 2. 둘째 줄부터 n-1개의 줄에 각 줄마다 후보지 쌍을 나타내는 두

codeup.kr

 

코드업을 밀던 중에 흥미로운 문제를 발견해서 포스팅한다.

다른 분들과 풀이의 방향이 달라 시간은 조금 느리지만, 다행히 코드는 깔끔하다. ㅎ

 

더보기

정해는 Union-Find 와 bfs 를 요구하지만, 사실 lca 만으로 풀린다!

생각의 편의를 위해 트리의 루트를 A로 만들어주자.

 

최적지의 집합이 (A-B 경로) $\cup $ (A-C 경로) 라는 것은 어렵지 않게 짐작 가능하다.

따라서 최적지의 개수는 $dep[a]+dep[b]-dep[lca(a,b)]+1$ 로 계산할 수 있다.

 

쿼리를 처리하는 것도 크게 복잡하지 않다.

후보지와 가장 가까운 최적지는 후보지에서 루트를 향해 올라가다가 처음으로 만나는 최적지이다.

이는 잘 생각해보면 B와의 LCA거나 A와의 LCA이고, 둘 중 루트에서 더 먼, 즉 깊이가 더 깊은 점이 정답임을 알 수 있다.

 

더보기
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1;
vector<int> ad[N];
int dep[N],par[N][17];
void make_tree(int cur,int prv) {
	for(auto nxt : ad[cur]){
		if(nxt==prv) continue;
		dep[nxt]=dep[cur]+1, par[nxt][0]=cur;
		make_tree(nxt,cur);
	}
}
int lca(int u,int v) {
	if(dep[u]<dep[v]) swap(u,v);
	int diff=dep[u]-dep[v];
	for(int i=0;i<17;i++) if(diff&(1<<i)) u=par[u][i];
	if(u==v) return u;
	for(int i=16;i>=0;i--)
		if(par[u][i]!=par[v][i]) u=par[u][i], v=par[v][i];
	return par[u][0];
}
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int n; cin >> n;
	for(int i=1;i<n;i++){
		int u,v; cin >> u >> v;
		ad[u].push_back(v);
		ad[v].push_back(u);
	}
	int a,b,c; cin >> a >> b >> c;
	make_tree(a,0);
	for(int j=1;j<17;j++) for(int i=1;i<=n;i++)
		par[i][j]=par[par[i][j-1]][j-1];
	cout << dep[b]+dep[c]-dep[lca(b,c)]+1 << '\n';
	int q; cin >> q;
	while(q--){
		int v; cin >> v;
		int x=lca(v,b), y=lca(v,c);
		if(dep[x]<dep[y]) swap(x,y);
		cout << x << '\n';
	}
}

'Problem Solving > CodeUp' 카테고리의 다른 글

1,200  (0) 2021.06.27
Well-Known Sequence  (0) 2021.03.20
1,000문제 달성  (2) 2021.02.15