Tarjan算法求解最近公共祖先问题
2015-09-05 10:56
411 查看
起因是因为一道树形DP。。要用到求双连通分量和双连通缩点算法的算法。。于是学了一发。。
Tarjan算法可求解最近公共祖先问题(LCA),即给一颗有根树,给出若干个查询(u,v),每次查询给出u和v的最近公共祖先。
这个算法是基于dfs和并查集的。最开始从根开始搜索,对每个子树进行深搜。由于是后序遍历,那么可以发现这样一个性质,对于一个查询(u,v),假设其公共祖先为w,那么当访问到w时,必定已经访问完w所有的子树。根据这样一条性质,当我们每次访问完w的一棵子树时,就可以将该子树放入w所在的集合,这一操作可通过并查集完成。当访问完所有的子树后,所有子树的节点的最近公共祖先显然为w,这时可以根据查询求出答案并存储,不难看出这一算法是离线的。也有在线的算法,有兴趣的可以自行百度。
总的时间复杂度为O(n+q),dfs的时间复杂度为O(n),q次查询复杂度为O(q)。
Tarjan算法还可以用于求解双连通分量、割点、桥等各种问题,有兴趣的话可以学习一下。
例题:最近公共祖先问题,HDU2586 http://acm.hdu.edu.cn/showproblem.php?pid=2586
HDU2586代码:
POJ 1470: http://poj.org/problem?id=1470
Tarjan算法可求解最近公共祖先问题(LCA),即给一颗有根树,给出若干个查询(u,v),每次查询给出u和v的最近公共祖先。
这个算法是基于dfs和并查集的。最开始从根开始搜索,对每个子树进行深搜。由于是后序遍历,那么可以发现这样一个性质,对于一个查询(u,v),假设其公共祖先为w,那么当访问到w时,必定已经访问完w所有的子树。根据这样一条性质,当我们每次访问完w的一棵子树时,就可以将该子树放入w所在的集合,这一操作可通过并查集完成。当访问完所有的子树后,所有子树的节点的最近公共祖先显然为w,这时可以根据查询求出答案并存储,不难看出这一算法是离线的。也有在线的算法,有兴趣的可以自行百度。
总的时间复杂度为O(n+q),dfs的时间复杂度为O(n),q次查询复杂度为O(q)。
Tarjan算法还可以用于求解双连通分量、割点、桥等各种问题,有兴趣的话可以学习一下。
例题:最近公共祖先问题,HDU2586 http://acm.hdu.edu.cn/showproblem.php?pid=2586
HDU2586代码:
// Header. Tarjan #include <algorithm> #include <iostream> #include <sstream> #include <cstring> #include <cstdio> #include <vector> #include <string> #include <bitset> #include <queue> #include <stack> #include <cmath> #include <ctime> #include <set> #include <map> using namespace std; // Macro typedef long long LL; #define TIME cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s." << endl; #define IN freopen("/Users/apple/input.txt", "r", stdin); #define OUT freopen("/Users/apple/out.txt", "w", stdout); #define mem(a, n) memset(a, n, sizeof(a)) #define rep(i, n) for(int i = 0; i < (n); i ++) #define repD(i, n) for(int i = (n); i; i --) #define REP(i, t, n) for(int i = (t); i < (n); i ++) #define REPD(i, t, n) for(int i = (n); i > (t); i --) #define FOR(i, t, n) for(int i = (t); i <= (n); i ++) #define FORD(i, t, n) for(int i = (n); i >= (t); i --) #define ALL(v) v.begin(), v.end() #define Min(a, b) a = min(a, b) #define Max(a, b) a = max(a, b) #define put(a) printf("%d\n", a) #define ss(a) scanf("%s", a) #define si(a) scanf("%d", &a) #define sii(a, b) scanf("%d%d", &a, &b) #define siii(a, b, c) scanf("%d%d%d", &a, &b, &c) #define VI vector<int> #define pb push_back #define x first #define y second const int inf = 0x3f3f3f3f, N = 4e4 + 5, MOD = 1e9 + 7; // Macro end int T, cas = 0; int n, m, par , vis , dis , ans ; vector<pair<int, int> > son ; vector<pair<int, int> > query ; // Imp int find(int x) { if(x != par[x]) return par[x] = find(par[x]); return x; } void Tarjan(int u) { vis[u] = 1; par[u] = u; int len = son[u].size(); rep(i, len) { int v = son[u][i].x; if(!vis[v]) { dis[v] = dis[u] + son[u][i].y; Tarjan(v); par[v] = u; } } len = query[u].size(); rep(i, len) { int v = query[u][i].x, idx = query[u][i].y; if(vis[v]) ans[idx] = dis[u] + dis[v] - 2 * dis[find(v)]; } } #define LOCAL int main(){ #ifdef LOCAL IN // OUT #endif si(T); while(T --) { sii(n, m); int u, v, d; rep(i, N) son[i].clear(), query[i].clear(); mem(vis, 0); rep(i, n - 1) { siii(u, v, d); son[u].pb(make_pair(v, d)); son[v].pb(make_pair(u, d)); } rep(i, m) { sii(u, v); query[u].pb(make_pair(v, i)); query[v].pb(make_pair(u, i)); } mem(vis, 0); dis[1] = 0; Tarjan(1); rep(i, m) put(ans[i]); } return 0; }
POJ 1470: http://poj.org/problem?id=1470
// Header. #include <algorithm> #include <iostream> #include <sstream> #include <cstring> #include <cstdio> #include <vector> #include <string> #include <bitset> #include <queue> #include <stack> #include <cmath> #include <ctime> #include <set> #include <map> using namespace std; // Macro typedef long long LL; #define TIME cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s." << endl; #define IN freopen("/Users/apple/input.txt", "r", stdin); #define OUT freopen("/Users/apple/out.txt", "w", stdout); #define mem(a, n) memset(a, n, sizeof(a)) #define rep(i, n) for(int i = 0; i < (n); i ++) #define repD(i, n) for(int i = (n); i; i --) #define REP(i, t, n) for(int i = (t); i < (n); i ++) #define REPD(i, t, n) for(int i = (n); i > (t); i --) #define FOR(i, t, n) for(int i = (t); i <= (n); i ++) #define FORD(i, t, n) for(int i = (n); i >= (t); i --) #define ALL(v) v.begin(), v.end() #define Min(a, b) a = min(a, b) #define Max(a, b) a = max(a, b) #define put(a) printf("%d\n", a) #define ss(a) scanf("%s", a) #define si(a) scanf("%d", &a) #define sii(a, b) scanf("%d%d", &a, &b) #define siii(a, b, c) scanf("%d%d%d", &a, &b, &c) #define VI vector<int> #define pb push_back const int inf = 0x3f3f3f3f, N = 1e3 + 5, MOD = 1e9 + 7; // Macro end int T, cas = 0; int n, m, ne; int head , vis , ans , fa , in ; VI query ; struct node { int v, next; }e[4 * N]; // Imp void addEdge(int u, int v) { e[ne].v = v, e[ne].next = head[u], head[u] = ne ++; } int find(int x) { if(x != fa[x]) return fa[x] = find(fa[x]); return x; } void Tarjan(int u) { fa[u] = u; for(int i = head[u]; i != -1; i = e[i].next) { int v = e[i].v; if(!vis[v]) { Tarjan(v); fa[v] = u; } } vis[u] = 1; int len = query[u].size(); rep(i, len) { int v = query[u][i]; int k = find(v); if(vis[v]) ans[k] ++; } } void init() { ne = 0; mem(in, 0); mem(ans, 0); mem(vis, 0); mem(head, -1); rep(i, N) query[i].clear(); } int main(){ #ifdef LOCAL IN // OUT #endif while(si(n) != EOF) { init(); int u, v; rep(i, n) { scanf("%d:(%d) ", &u, &m); rep(j, m) si(v), in[v] ++, addEdge(u, v); } si(m); rep(i, m) { while(getchar() != '('); scanf("%d %d", &u, &v); query[u].pb(v); query[v].pb(u); while(getchar() != ')'); } FOR(i, 1, n) if(!in[i]) { Tarjan(i); break; } FOR(i, 1, n) if(ans[i]) printf("%d:%d\n", i, ans[i]); } return 0; }
相关文章推荐
- 【POJ2289】【多重匹配】【二分】【模板】
- Java 解析chm文件实战(原创)
- 剑指offer——面试题42:(一)翻转单词顺序
- PHP之——官方手册下载地址
- 乱七八糟记事本
- HDU1556 color the ball(区间修改,单点查询)
- 用户及用户组管理:useradd,psswd,groupadd
- 『细节』是什么
- 『细节』是什么
- 黑马程序员——CSS
- C++11 auto和decltype推导规则
- 数据结构之反黄牛火车票订票系统
- Mysql字符转义
- C++11 auto和decltype推导规则
- EF 常见语句以及sql语句简单 后续继续添加
- acm的一点思考
- ActionScript 3.0 学习(十) AS3 模拟转盘抽奖小程序
- glCleatDepth
- glCleatDepth
- 数据结构在软件编程中的地位