HDU 2586 How far away? LCA 离线tarjan
2014-11-11 15:48
489 查看
题意:给出一个树,树上每条边都有距离,询问某些点之间的距离。
思路:我们可以任意指定一点为根,那两点之间的距离就是dist[u]+dist[v] - 2 * dis[lca(u,v)];
因为询问直接给出,我们可以用离线的tarjan算法来求。
tarjan算法的整体思路是这样的:dfs这个树,在dfs的过程中将可能查询到的点且结果相同的节点加入并查集中。
这样,对于某个查询lca(u,v).假设算法的前面已经处理完了u,正在处理v。那么此时在并查集中find(u)就是u,v的最近公共祖先。需要非常注意的一点是,此处不应该是find(v)。因为v是正在处理的节点,v还没有加入并查集,所以find(v)返回的是错误的答案。
在算法中需要非常注意的一点是,要先遍历子树,在把该节点加入到以父亲为根的集合中。
算法可以这样解释:
利用递归的LCA过程。
当lca(u)执行完毕后,以u为根的子树已经全部并为了一个集合。而一个lca的内部实际上做了的事就是对其子结点,依此调用lca.
当v1(第一个子结点)被lca,正在处理v2的时候,以v1为根的子树+u同在一个集合里,f(u)+编号比u小的u的兄弟的子树 同在一个集合里,f(f(u)) + 编号比f(u)小的 f(u)的兄弟 的子树 同在一个集合里……
而这些集合,对于v2的LCA都是不同的。因此只要查询x在哪一个集合里,就能知道LCA(v2,x)
还有一种可能,x不在任何集合里。当他是v2的儿子,v3,v4等子树或编号比u大的u的兄弟的子树(等等)时,就会发生这种情况。即还没有被处理。还没有处理过的怎么办?把一个查询(x1,x2)往查询列表里添加两次,一次添加到x1的列表里,一次添加到x2的列表里,如果在做x1的时候发现 x2已经被处理了,那就接受这个询问。(两次中必定只有一次询问被接受).
代码如下:
思路:我们可以任意指定一点为根,那两点之间的距离就是dist[u]+dist[v] - 2 * dis[lca(u,v)];
因为询问直接给出,我们可以用离线的tarjan算法来求。
tarjan算法的整体思路是这样的:dfs这个树,在dfs的过程中将可能查询到的点且结果相同的节点加入并查集中。
这样,对于某个查询lca(u,v).假设算法的前面已经处理完了u,正在处理v。那么此时在并查集中find(u)就是u,v的最近公共祖先。需要非常注意的一点是,此处不应该是find(v)。因为v是正在处理的节点,v还没有加入并查集,所以find(v)返回的是错误的答案。
在算法中需要非常注意的一点是,要先遍历子树,在把该节点加入到以父亲为根的集合中。
算法可以这样解释:
利用递归的LCA过程。
当lca(u)执行完毕后,以u为根的子树已经全部并为了一个集合。而一个lca的内部实际上做了的事就是对其子结点,依此调用lca.
当v1(第一个子结点)被lca,正在处理v2的时候,以v1为根的子树+u同在一个集合里,f(u)+编号比u小的u的兄弟的子树 同在一个集合里,f(f(u)) + 编号比f(u)小的 f(u)的兄弟 的子树 同在一个集合里……
而这些集合,对于v2的LCA都是不同的。因此只要查询x在哪一个集合里,就能知道LCA(v2,x)
还有一种可能,x不在任何集合里。当他是v2的儿子,v3,v4等子树或编号比u大的u的兄弟的子树(等等)时,就会发生这种情况。即还没有被处理。还没有处理过的怎么办?把一个查询(x1,x2)往查询列表里添加两次,一次添加到x1的列表里,一次添加到x2的列表里,如果在做x1的时候发现 x2已经被处理了,那就接受这个询问。(两次中必定只有一次询问被接受).
代码如下:
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int MAX = 40010; int head[MAX],to[MAX<<2],nxt[MAX<<2],len[MAX<<2],tot; int head1[MAX],to1[MAX<<2],nxt1[MAX<<2],idx[MAX<<2],tot1; int record[MAX][3]; int dis[MAX]; bool vis[MAX]; int fath[MAX]; int T,u,c,v,N,M; void init() { tot = tot1 = 0; memset(head,-1,sizeof(head)); memset(head1,-1,sizeof(head1)); memset(vis,0,sizeof(vis)); for(int i = 1; i <= N; ++i) fath[i] = i; } int find(int u) { return u == fath[u]? u : fath[u] = find(fath[u]); } void addedge(int u, int v, int c) { to[tot] = v, len[tot] = c; nxt[tot] = head[u], head[u] = tot++; to[tot] = u, len[tot] = c; nxt[tot] = head[v], head[v] = tot++; } void addedge1(int u, int v, int id) { to1[tot1] = v, idx[tot1] = id; nxt1[tot1] = head1[u],head1[u] = tot1++; to1[tot1] = u, idx[tot1] = id; nxt1[tot1] = head1[v], head1[v] = tot1++; } void dfs(int u) { vis[u] = true; for(int i = head[u]; ~i; i = nxt[i]){ int v = to[i]; if(vis[v]) continue; dis[v] = dis[u] + len[i]; dfs(v); fath[v] = u; } for(int i = head1[u]; ~i; i = nxt1[i]){ int v = to1[i]; if(vis[v]) record[idx[i]][2] = find(v); } } int main(void) { //freopen("input.txt","r",stdin); scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); init(); for(int i = 0; i < N - 1; ++i){ scanf("%d %d %d",&u,&v,&c); addedge(u,v,c); } for(int i = 0; i < M; ++i){ scanf("%d%d",&u,&v); addedge1(u,v,i); } dis[1] = 0; dfs(1); for(int i = 0; i < M; ++i) printf("%d\n",dis[record[i][0]] + dis[record[i][1]] - 2 * dis[record[i][2]]); //printf("%d\n",record[i][2]); } return 0; }
相关文章推荐
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- HDU - 2586 How far away ?(tarjan离线做法求lca)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- HDU2586 How far away ?(最近公共祖先lca,离线Tarjan,最短路)
- HDU - 2586 How far away ?(LCA tarjan离线做法)
- hdu 2586 How far away ?(离线tarjan求LCA)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- HDU 2586 How far away ? LCA离线tarjan思想
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)
- hdu 2586 How far away ? (LCA 离线tarjan)
- hdu 2586 How far away ?(在线LCA+离线Tarjan)