codeforces gym-101741 Cover the Paths LCA、离线询问
題目
題目鏈接
題意
給出一棵樹。
給出一堆路徑,找出最少一個(gè)最少的點(diǎn)的集合,使得所有的路徑都經(jīng)過集合中的至少一個(gè)點(diǎn)。
題解
考慮一個(gè)路徑的兩個(gè)端點(diǎn),從下往上最后一個(gè)能被經(jīng)過的點(diǎn)就是這兩點(diǎn)的LCA。
因此我們做整棵樹dfs,并在回溯的時(shí)候判斷當(dāng)前節(jié)點(diǎn)是否為某路徑的LCA,如果是的話,那么這個(gè)點(diǎn)一定要被取到,不然會(huì)至少存在一個(gè)路徑不被集合的點(diǎn)覆蓋。
取到這個(gè)點(diǎn)以后,需要把這個(gè)點(diǎn)經(jīng)過的所有路徑都給取消掉。
這個(gè)題并不需要實(shí)際去求LCA,如果某條路徑詢問同時(shí)存在于兩顆子樹中,那么說明當(dāng)前節(jié)點(diǎn)即這條路徑兩個(gè)端點(diǎn)的LCA。
因此只需要對(duì)于每個(gè)節(jié)點(diǎn)維護(hù)一個(gè)詢問標(biāo)號(hào)的集合就可以了。這樣當(dāng)?shù)谝淮斡龅酵粋€(gè)標(biāo)號(hào)出現(xiàn)在兩個(gè)將要被合并的set中,那么這個(gè)節(jié)點(diǎn)即是LCA。
每次回溯的時(shí)候合并子樹與父節(jié)點(diǎn)的集合(需要啟發(fā)式合并)。
代碼
#include <iostream> #include <cstdio> #include <algorithm> #include <vector> #include <unordered_set> using namespace std; const int maxn = 1e5+7; int n,m; vector<int> G[maxn]; unordered_set<int> st[maxn]; int ok[maxn]; void dfs(int u,int p){for(auto v : G[u]){if(v == p) continue;dfs(v,u);if(ok[u]) continue;if(st[u].size() < st[v].size()){swap(st[u],st[v]);}for(auto i : st[v]){if(st[u].find(i) != st[u].end()){ok[u] = 1;break;}st[u].insert(i);}}//如果該節(jié)點(diǎn)被取的,那么清空其詢問標(biāo)號(hào)集合,以表示把這個(gè)點(diǎn)經(jīng)過的所有路徑都標(biāo)記掉。if(ok[u]) st[u].clear();} int main(){scanf("%d",&n);for(int i = 0;i < n-1;++i){int u,v;scanf("%d%d",&u,&v);G[u].push_back(v);G[v].push_back(u);}scanf("%d",&m);for(int i = 0;i < m;++i){int u,v;scanf("%d %d",&u,&v);if(u == v) ok[u] = 1;st[u].insert(i);st[v].insert(i);}dfs(1,0);vector<int> ans;for(int i = 1;i <= n;++i){if(ok[i]) ans.push_back(i);}printf("%d\n",ans.size());for(auto i : ans)printf("%d ",i);return 0; }總結(jié)
以上是生活随笔為你收集整理的codeforces gym-101741 Cover the Paths LCA、离线询问的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 洛谷-P3203 弹飞绵羊 分块
- 下一篇: codeforces gym-10174