离线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"); else printf("%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); else lca = 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; }