POJ 1986 Distance Queries

2 篇文章 0 订阅

题意:求出两个树节点的距离

思路:可以转化成d[u]+d[v]-2*d[lca(u,v)]       d[i]代表根节点到i的距离    lca(u,v)就是u,v的最近公共祖先的意思

模板题

参考

参考

第一种是tarjan的,比较快

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N = 40005;
const int M = 20010;
int n, m, k, tot, ks;
int first[N], R[N<<1], head[N], dir[N<<1], vec[N<<1], _first[N], fa[N];
int dp[N<<1][25];
bool vis[N];
struct edge{
	int v, w, nex;
}e[N<<1];
struct ques{
	int v,lca,nex;
}eq[M];


void init(){
	memset(first, -1, sizeof(first));
	memset(vis,false,sizeof(vis));
	memset(_first, -1, sizeof(_first));
	dir[1] = tot = k = ks = 0;
}
inline void addedge(int x, int y, int z){
	e[k].v = y, e[k].w = z, e[k].nex = first[x];
	first[x] = k++;
	e[k].v = x, e[k].w = z, e[k].nex = first[y];
	first[y] = k++;
}
inline void addque(int x, int y){
	eq[ks].v = y, eq[ks].lca = -1, eq[ks].nex = _first[x];
	_first[x] = ks++;
	eq[ks].v = x, eq[ks].lca = -1, eq[ks].nex = _first[y];
	_first[y] = ks++;
}
int find(int x){
	return x == fa[x]? x : fa[x] = find(fa[x]);
}
void tarjan(int u){
	vis[u] = true;
	fa[u] = u;
	for(int i = first[u]; ~i; i = e[i].nex){
		int v = e[i].v, w = e[i].w;
		if(!vis[v]){
			dir[v] = dir[u] + w;
			tarjan(v);
			fa[v] = u;
		}
	}
	for(int i = _first[u]; ~i; i = eq[i].nex){
		int v = eq[i].v;
		if(vis[v]){
			eq[i].lca = eq[i^1].lca = find(eq[i].v);
		}
	}
}
int main(){
	int x,y,z;
	char temp[5];
	while(~scanf("%d%d", &n, &m)){
		init();
		for(int i = 0; i < m; i++){
			scanf("%d%d%d %s", &x, &y, &z, temp);
			addedge(x,y,z);
		}
        int q;
        scanf("%d",&q);
        for(int i = 0; i < q; i++){
            int u,v;
            scanf("%d%d",&u,&v);
            addque(u,v);
        }
        tarjan(1);
        for(int i = 0; i < q; i++){
			int s = i*2 , u =eq[s].v, v = eq[s+1].v, lca = eq[s].lca;
			printf("%d\n",dir[u] + dir[v] - 2*dir[lca]);
        }
	}
	return 0;
}



第二种是RMQ的思想

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N = 40005;
const int M = 25;
int n, m, k, tot;
int first[N], R[N<<1], head[N], dir[N<<1], vec[N<<1];
int dp[N<<1][25];
bool vis[N];
struct edge{
	int v, w, nex;
}e[N<<1];
inline void addedge(int x, int y, int z){
	e[k].v = y, e[k].w = z, e[k].nex = first[x];
	first[x] = k++;
	e[k].v = x, e[k].w = z, e[k].nex = first[y];
	first[y] = k++;
}
void init(){
	memset(first, -1, sizeof(first));
	memset(vis,false,sizeof(vis));
	dir[1] = tot = k = 0;
}
void dfs(int u, int dep){
	vis[u] = true, head[u] = ++tot, vec[tot] = u,R[tot] = dep;
	for(int i = first[u]; ~i; i = e[i].nex){
		int v = e[i].v, w = e[i].w;
		if(!vis[v]){
			dir[v] = dir[u] + w;
			dfs(v,dep+1);
			vec[++tot] = u,R[tot] = dep;
		}
	}
}
void ST(int len){
	int p = (int) (log((double)(len))/log(2.0));
	for(int i = 1; i <= len; i++)dp[i][0] = i;
	for(int j = 1; j <= p; j++){
		for(int i = 1; i+(1<<j)-1 <= len; i++){
			dp[i][j] = R[dp[i][j-1]] < R[dp[i+(1<<(j-1))][j-1]]? dp[i][j-1] : dp[i+(1<<(j-1))][j-1];
		}
	}
}
int rmq(int x, int y){
	int p = (int)(log((double)(y-x+1))/log(2.0));
	int a = dp[x][p] , b = dp[y-(1<<p)+1][p];
	return R[a] < R[b] ? a:b;
}
int LCA(int u, int v){
	int x = head[u], y = head[v];
	if(x > y)swap(x,y);
	return vec[rmq(x,y)];
}
int main(){
	int x,y,z;
	char temp[5];
	while(~scanf("%d%d", &n, &m)){
		init();
		for(int i = 0; i < m; i++){
			scanf("%d%d%d %s", &x, &y, &z, temp);
			addedge(x,y,z);
		}
		dfs(1,1);
		ST(tot);
        int q;
        scanf("%d",&q);
        while(q--){
            int u,v,lca;
            scanf("%d%d",&u,&v);
            lca = LCA(u,v);
            printf("%d\n",dir[u] + dir[v] - 2*dir[lca]);
        }
	}
	return 0;
}


第三种是倍增的方法

参考

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N = 50005;
const int M = 50;
int n, m, k, tot;
int first[N], dep[N], dir[N], fa[N][M];
bool vis[N];
struct edge{
	int v, w, nex;
}e[N<<1];
inline void addedge(int x, int y, int z){
	e[k].v = y, e[k].w = z, e[k].nex = first[x];
	first[x] = k++;
	e[k].v = x, e[k].w = z, e[k].nex = first[y];
	first[y] = k++;
}
void init(){
	memset(first, -1, sizeof(first));
	memset(vis, false, sizeof(vis));
	memset(fa, -1, sizeof(fa));
	dir[1] = tot = k = 0;
}
void dfs(int u, int d){
	vis[u] = true,dep[u] = d;
	for(int i = first[u]; ~i; i = e[i].nex){
		int v = e[i].v, w = e[i].w;
		if(!vis[v]){
			dir[v] = dir[u] + w;
			fa[v][0] = u;
			dfs(v,d+1);
		}
	}
}
void init_fa(){
	for(int j = 1; (1<<j) <= n; j++){
		for(int i = 1; i <= n; i++){
			fa[i][j] = fa[fa[i][j-1]][j-1];
		}
	}
}
int LCA(int a, int b){
	int i, j;
	if(dep[a] < dep[b])swap(a, b);
	for(i = 0; (1<<i) <= dep[a]; i++);i--;
	for( j = i; j >= 0; j--){
		if(dep[a] - (1<<j) >= dep[b])
			a = fa[a][j];
	}
	if(a == b)return a;
	for(int j = i; j >= 0; j--){
		if(fa[a][j] != -1 && fa[a][j] != fa[b][j]){
			a = fa[a][j], b = fa[b][j];
		}
	}
	return fa[a][0];
}
int main(){
    int x,y,z;
    char temp[5];
    while(~scanf("%d%d", &n, &m)){
        init();
        for(int i = 0; i < m; i++){
            scanf("%d%d%d %s", &x, &y, &z, temp);
            addedge(x,y,z);
        }
        dfs(1,0);
        init_fa();
        int q;
        scanf("%d",&q);
        while(q--){
            int u,v,lca;
            scanf("%d%d",&u,&v);
            lca = LCA(u,v);
            printf("%d\n",dir[u] + dir[v] - 2*dir[lca]);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值