tarjan 离线求 lca (专题)

来源:互联网 发布:歼20就是个笑话 知乎 编辑:程序博客网 时间:2024/06/01 11:17

离线LCA的求法,相信大家都知道使用tarjan。该方法确实很巧妙,利用dfs的性质,假设u的父亲为fa,当以u为根节点的子树被访问完之后,那么任何与u同属于同一个父亲fa并且不包含在u的子树内的点,与u子树内的任何一个点的最近公共祖先一定是fa,我们使用并查集维护同属一个ancestor的定点集合。


HDU 2586 How Far Away?

这道题是说给你一棵树,询问任意两点之间的最短距离。

显然dis[u][v] = (dep[u] - dep[lca]) + (dep[v] - dep[lca]),其中dep[u]表示u点到root的距离。

于是先dfs预处理dep数组,然后使用tarjan离线求解u and v之间的lca,并且记录结果。

#include <iostream>#include <cstdlib>#include <cstdio>#include <vector>#include <cstring>using namespace std;const int NMAX = 40010;typedef struct NODE{int v, dis;NODE(int tv, int tdis){v = tv;dis = tdis;}}Node;vector<Node> adj[NMAX];typedef struct QUERY_NODE{int v, index;QUERY_NODE(int tv, int tindex){v = tv;index = tindex;}}QueryNode;vector<QueryNode> query[NMAX];int ancestor[NMAX];int tree[NMAX];bool vis[NMAX];int dep[NMAX];int find(int u){int r = u;while(tree[r] != r){r = tree[r];}while(u != r){int temp = tree[u];tree[u] = r;u = temp;}return r;}void init(){memset(vis, false, sizeof(vis));for(int i=0; i<NMAX; i++){adj[i].clear();query[i].clear();}}void tarjan(int fa, int u){tree[u] = u;//vis[u] = true;int len = adj[u].size();for(int i=0; i<len; i++){int v = adj[u][i].v;if(v != fa){tarjan(u, v);tree[find(v)] = u;}}vis[u] = true;len = query[u].size();for(int i=0; i<len; i++){int v = query[u][i].v;int index = query[u][i].index;if(vis[v]){ancestor[index] = find(v);int lca = ancestor[index];ancestor[index] = (dep[u]-dep[lca]) + (dep[v] - dep[lca]);//printf("%d %d %d %d\n", u, v, lca, ancestor[index]);}}}int getDis(int fa, int u){int len = adj[u].size();for(int i=0; i<len; i++){int v = adj[u][i].v;int dis = adj[u][i].dis;if(v != fa){dep[v] = dep[u] + dis;getDis(u, v);}}}int main(){//freopen("data.in", "r", stdin);int T;scanf("%d", &T);int n, k;while(T--){scanf("%d%d", &n, &k);init();int u, v, w;for(int i=1; i<n; i++){scanf("%d%d%d", &u, &v, &w);adj[u].push_back(Node(v, w));adj[v].push_back(Node(u, w));}for(int i=0; i<k; i++){scanf("%d%d", &u, &v);query[u].push_back(QueryNode(v, i));query[v].push_back(QueryNode(u, i));}memset(dep, 0, sizeof(dep));getDis(-1, 1);tarjan(-1, 1);for(int i=0; i<k; i++)printf("%d\n", ancestor[i]);}return 0;}

HDU 2874 connections between cities

这道题何上一题差不多,题意是说:给了你很多棵树,询问任意两点:如果不在同一棵树,那么输出not connected;否则输出两点之间的距离,和上一题做法一样。

#include <iostream>#include <cstdlib>#include <cstdio>#include <vector>#include <cstring>using namespace std;const int NMAX = 10010;typedef struct NODE{int v, dis;NODE(int tv, int tdis){v = tv;dis = tdis;}}Node;vector<Node> adj[NMAX];typedef struct QUERY_NODE{int v, index;QUERY_NODE(int tv, int tindex){v = tv;index = tindex;}}QueryNode;vector<QueryNode> query[NMAX];int ancestor[1000006];int tree[NMAX];bool vis[NMAX], visited[NMAX];int dep[NMAX];int find(int u){int r = u;while(tree[r] != r){r = tree[r];}while(u != r){int temp = tree[u];tree[u] = r;u = temp;}return r;}void init(){memset(visited, false, sizeof(visited));for(int i=0; i<NMAX; i++){adj[i].clear();query[i].clear();}}void tarjan(int fa, int u){visited[u] = true;tree[u] = u;int len = adj[u].size();for(int i=0; i<len; i++){int v = adj[u][i].v;if(v != fa){tarjan(u, v);tree[find(v)] = u;}}vis[u] = true;len = query[u].size();for(int i=0; i<len; i++){int v = query[u][i].v;int index = query[u][i].index;if(vis[v]){ancestor[index] = find(v);int lca = ancestor[index];ancestor[index] = (dep[u]-dep[lca]) + (dep[v] - dep[lca]);//printf("%d %d %d %d\n", u, v, lca, ancestor[index]);}}}int getDis(int fa, int u){int len = adj[u].size();for(int i=0; i<len; i++){int v = adj[u][i].v;int dis = adj[u][i].dis;if(v != fa){dep[v] = dep[u] + dis;getDis(u, v);}}}int main(){//freopen("data.in", "r", stdin);int n, m, k;while(scanf("%d%d%d", &n, &m, &k) != EOF){init();int u, v, w;for(int i=0; i<m; i++){scanf("%d%d%d", &u, &v, &w);adj[u].push_back(Node(v, w));adj[v].push_back(Node(u, w));}for(int i=0; i<k; i++){scanf("%d%d", &u, &v);query[u].push_back(QueryNode(v, i));query[v].push_back(QueryNode(u, i));}memset(dep, 0, sizeof(dep));memset(ancestor, -1, sizeof(ancestor));for(int i=1; i<=n; i++){if(!visited[i]){memset(vis, false, sizeof(vis));getDis(-1, i);tarjan(-1, i);}}for(int i=0; i<k; i++){if(ancestor[i] == -1)printf("Not connected\n");elseprintf("%d\n", ancestor[i]);}}return 0;}

HDU 4547 CD操作

这道大水题,被坑爹了。输入的查询中存在之前目录中没有出现的点,然后就是输入肯定是保证这两个没有出现过的点相同,所以初始化的时候将答案全初始化为0就行了。

坑爹了……

奉上代码吧。

#include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>#include <vector>#include <map>using namespace std;const int MAX = 100010;vector<int> adj[MAX];vector<int> queryid[MAX];int dep[MAX], ans[MAX];bool vis[MAX];typedef struct QUERY{int a, b;QUERY(){}QUERY(int ta, int tb){a = ta;b = tb;}}Query;Query query[MAX];map<string, int> hash_name;void tarjan(int root);void init(int n, int m){memset(vis, false, sizeof(vis));hash_name.clear();for(int i=1; i<MAX; i++){adj[i].clear();queryid[i].clear();}char sa[50], sb[50];int cnt = 0;int u, v;for(int i=1; i<n; i++){scanf("%s %s", sa, sb);if(hash_name[sa] == 0)hash_name[sa] = ++cnt;u = hash_name[sa];if(hash_name[sb] == 0)hash_name[sb] = ++cnt;v = hash_name[sb];adj[v].push_back(u);vis[u] = true;}for(int i=0; i<m; i++){scanf("%s %s", sa, sb);if(hash_name[sa] == 0)hash_name[sa] = ++cnt;u = hash_name[sa];if(hash_name[sb] == 0)hash_name[sb] = ++cnt;v = hash_name[sb];query[i] = Query(u, v);queryid[u].push_back(i);queryid[v].push_back(i);}memset(ans, 0, sizeof(ans));int root;for(int i=1; i<=n; i++){if(!vis[i]){root = i;break;}}memset(vis, false, sizeof(vis));tarjan(root);}int tree[MAX];int find(int u){int root = u;while(tree[root] != root)root = tree[root];while(u != root){int temp = tree[u];tree[u] = root;u = temp;}return root;}void tarjan(int u){int len = adj[u].size();tree[u] = u;for(int i=0; i<len; i++){int v = adj[u][i];dep[v] = dep[u]+1;tarjan(v);tree[find(v)] = u;}vis[u] = true;len = queryid[u].size();for(int i=0; i<len; i++){int id = queryid[u][i];int a = query[id].a;int b = query[id].b;int lca;if(u == a)lca = find(b);elselca = find(a);ans[id] = dep[a] - dep[lca];if(lca != b)ans[id]++;}}int main(){int T;scanf("%d", &T);while(T--){int n, m;scanf("%d%d", &n, &m);init(n, m);for(int i=0; i<m; i++)printf("%d\n", ans[i]);}return 0;}


原创粉丝点击