Solução por Pedro Racchetti
Conhecimentos utilizados:
Para esse problema, primeiro temos que perceber que para qualquer caminho, podemos simplesmente usar o menor ancestral comum (LCA) entre os dois extremos, se considerarmos a raiz como um nó qualquer (iremos usar o , a título de implementação).
Podemos então perceber que se considerarmos a sub-árvore do LCA de um caminho, e algum outro caminho com o LCA de altura menor que a altura de , e um dos pontos extremos de estiver na sub-árvore de , não precisamos incluir no conjunto resposta! Portanto, quando processarmos um caminho, podemos verificar se qualquer um dos dois extremos já foram marcados. Se não, inserimos o LCA no conjunto resposta, e marcamos todos os vértices da sub-árvore dele.
Esse algoritmo porém fica muito demorados. Para acelerá-lo, podemos linearizar a árvore da seguinte maneira: guardaremos uma variável . Sempre que chamarmos uma nova recursão na DFS na vértice , incrementamos o por 1, e guardaremos esse valor no vetor . Quando sair de uma DFS, guardaremos o no vetor . Nessa linearização, uma vértice está na sub-árvore de se . Podemos usar essa propriedade, para usarmos uma BIT de marcação, com esse vetor. Sempre que adicionarmos um LCA no conjunto, podemos fazer um update de valor na posição na BIT, e -1 na posição . Para verificarmos se um caminho ja foi marcado, basta checar com uma query se a posição ou é maior que ou igual à 1.
Segue o código, comentado, para melhor compreensão:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<bits/stdc++.h> | |
using namespace std; | |
const int MAXN = 1e5 + 10; | |
typedef long long ll; | |
int bit[MAXN]; | |
int T; | |
void update(int idx, ll val){ | |
while(idx <= T){ | |
bit[idx] += val; | |
idx += (idx&-idx); | |
} | |
} | |
ll query(int idx){ | |
ll ans = 0; | |
while(idx > 0){ | |
ans += bit[idx]; | |
idx -= (idx&-idx); | |
} | |
return ans; | |
} | |
vector<int> grafo[MAXN]; | |
int dp[25][MAXN]; | |
int tin[MAXN], tout[MAXN], altura[MAXN]; | |
void dfs(int x, int p){ | |
tin[x] = ++T; | |
for(int i = 1; i <= 21; i++){ | |
dp[i][x] = dp[i-1][dp[i-1][x]]; | |
} | |
for(int i = 0; i < grafo[x].size(); i++){ | |
int viz = grafo[x][i]; | |
if(viz == p) continue; | |
altura[viz] = altura[x] + 1; | |
dp[0][viz] = x; | |
dfs(viz, x); | |
} | |
tout[x] = T; | |
} | |
int LCA(int a, int b){ | |
if(altura[a] < altura[b]) swap(a, b); | |
int dist = altura[a] - altura[b]; | |
for(int k = 0; k <= 21; k++ ){ | |
if(dist&(1<<k)) a= dp[k][a]; | |
} | |
if(a == b) return a; | |
for(int k = 21; k >= 0; k--){ | |
if(dp[k][a] == dp[k][b]) continue; | |
a = dp[k][a]; | |
b = dp[k][b]; | |
} | |
return dp[0][a]; | |
} | |
bool cmp(pair<int, int> a, pair<int, int> b){ | |
// essa funcao sera usada na ordenacao, para que ordenemos os caminhos pela maior altura | |
return altura[a.first] > altura[b.first]; | |
} | |
int n; | |
vector<int> ans; | |
pair<int, int > caminhos[MAXN], pontos[MAXN]; | |
int main(){ | |
scanf("%d", &n); | |
for(int i = 1; i < n; i++){ | |
int u, v; | |
scanf("%d %d", &u, &v); | |
//inicializamos a arvore | |
grafo[u].push_back(v); | |
grafo[v].push_back(u); | |
} | |
//Inicializamos a tabela esparsa para o LCA | |
//e as alturas dos caminhos | |
dfs(1, 0); | |
int m; | |
scanf("%d" , &m); | |
for(int i = 0; i < m; i++){ | |
int a, b; | |
scanf("%d %d", &a, &b); | |
//iremos guardar os caminhos com o LCA e os pontos que os compoem | |
caminhos[i] = make_pair(LCA(a, b), i); | |
pontos[i] = make_pair(a, b); | |
} | |
//Ordenamos os caminhos pela maior altura do lca | |
sort(caminhos, caminhos + m, cmp); | |
for(int i = 0; i < m; i++){ | |
int a = pontos[caminhos[i].second].first, b = pontos[caminhos[i].second].second; | |
int lc = caminhos[i].first; | |
//verificamos se esses pontos estão em alguma sub-árvore já marcada, os ignoramos | |
if(query(tin[a]) >= 1|| query(tin[b]) >= 1 ) continue; | |
//inserimos esse lca no conjunto resposta | |
ans.push_back(lc); | |
update(tin[lc], 1); | |
update(tout[lc] + 1, -1); | |
} | |
//imprimimos o conjunto resposta | |
printf("%d\n", ans.size()); | |
for(int i = 0; i < ans.size(); i++) printf("%d ",ans[i] ); | |
printf("\n"); | |
} |