https://www.acmicpc.net/problem/18263
솔루션 참고. 오프라인 쿼리 중 처음 보는 접근.
온라인 쿼리로 풀 수는 있다고 하지만 떠오르지 않는다
<문제설명>
트리가 주어지고, 각 정점마다 type이 주어진다.
(정점, type 모두 최대 1e5)
최대 1e5개의 쿼리가 들어온다.
u, v, t : u~v 경로상에 type t가 존재하는가?
<풀이>
트리의 루트부터 차례로 순회하면서, 현재 정점이 u일때, u가 끝점인 쿼리들을 처리해 줄 것.
dat[i][0], dat[i][1] : i번째 쿼리의 첫번째 끝점, 두번째 끝점
vector<int> todo[u] : u가 끝점인 쿼리들의 인덱스.
w = lca(u,v)라고 할 때, 다음 그림의 색칠한 부분에 쿼리에서 물어본 타입이 있는지 처리해주면 된다.
dfs를 돌며 현재 정점이 u일 때, (1~u) 경로 중 type t를 갖는 정점들을 구분한 벡터 sav[t]를 유지해준다. (2차원 배열 안에 최대 n개 정점 들어가니 메모리 괜찮음)
처리중인 쿼리에서 물어본 type t를 갖는 정점이 sav[t]에 있다면 sva[t]에 가장 마지막에 추가된 요소가 u와 가장 가까운 점이므로 그 정점 x = sav[t].back()에 대해서만 생각해주면 된다.
x가 v의 조상이 아니라면 x는 색칠한 영역에 있는 것이므로 해당 쿼리는 참이 된다.
w의 type이 t라면 해당 쿼리는 참이다.
v~w 영역은 나중에 dfs로 v를 방문할 때 처리해준다.
<코드>
const int MAX = 1e5 + 4;
int n, m, breed[MAX], p[MAX][20], tin[MAX], tout[MAX], counter;
int dat[MAX][2], c[MAX]; // query;
bool ans[MAX];
vector<int> g[MAX], todo[MAX];
void dfs(int u, int prv) {
tin[u] = counter++;
for (int v : g[u]) if (v != prv) {
p[v][0] = u;
for (int i = 1; (1 << i) < n; i++)
p[v][i] = p[p[v][i - 1]][i - 1];
dfs(v, u);
}
tout[u] = counter++;
}
bool isAncestor(int u, int v) { // u가 v의 조상인지
return tin[u] <= tin[v] && tout[v] <= tout[u];
}
int lca(int u, int v) {
if (isAncestor(u, v)) return u;
if (isAncestor(v, u)) return v;
for (int i = 19; i >= 0; i--)
if (p[u][i] != 0 && !isAncestor(p[u][i], v))
u = p[u][i];
return p[u][0];
}
vector<int> sav[MAX];
void dfs2(int u, int prv) {
sav[breed[u]].push_back(u);
for (int i : todo[u]) if (!sav[c[i]].empty()) { //u를 끝점으로 물어본 쿼리 처리
int v = dat[i][0] + dat[i][1] - u; //u가 아닌 다른 끝점
if (breed[u] == c[i]) ans[i] = true;
else if (breed[lca(u, v)] == c[i]) ans[i] = true;
else {
int x = sav[c[i]].back(); //색칠한 영역에 들어갈 후보 정점
if (!isAnscestor(x, v)) ans[i] = true; //w보다 depth가 낮은 경우
}
}
for (int v : g[u]) if (v != prv) dfs2(v, u);
sav[breed[u]].pop_back();
}
int main() {
FAST; cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> breed[i];
for (int i = 0, u, v; i < n - 1; i++) {
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
for (int i = 0; i < m; i++) {
cin >> dat[i][0] >> dat[i][1] >> c[i];
todo[dat[i][0]].push_back(i);
todo[dat[i][1]].push_back(i);
}
dfs2(1, 0);
for (int i = 0; i < m; i++) cout << ans[i];
cout << '\n';
}
<생각>
isAncestor(int u, int v) 만으로도 해결할 수 있는 것이 많은 것 같다.
'알고리즘 > 백준 & swacademy' 카테고리의 다른 글
BOJ 2336 - 굉장한 학생 (세그트리, 수학, 기하) (0) | 2020.04.22 |
---|---|
BOJ 18879 - The moo particle (기하, 수학) (0) | 2020.04.17 |
BOJ 11813 - GALAKSIJA (disjoint set, tree, 오프라인쿼리) (0) | 2020.03.18 |
BOJ 11510 - TOPOVI (constructive, greedy) (0) | 2020.03.11 |
BOJ 15759 - Talent show (냅색, 이분탐색) (0) | 2020.03.01 |