您的位置:首页 > 其它

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已经被处理了,那就接受这个询问。(两次中必定只有一次询问被接受).

代码如下:

#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;
}



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: