您的位置:首页 > 产品设计 > UI/UE

POJ 1984 Distance Queries LCA

2012-04-11 18:52 393 查看
http://poj.org/problem?id=1986

题意:给你一颗树,任意给定Q次询问(a,b) ,询问a 和b两个结点之间的距离。N<=40000 , Q<=40000

思路:LCA,因为是一颗树,我们可以任意选取一个结点作为树的根,然后用一次dfs,在O(E)的时间内求出所有点到根的距离,然后用LCA的Tarjin离线算法求出Q次询问的最近公共祖先,最后dis[i][j] = dis[i] + dis[j] - dis[ LCA(i,j) ] ;dis[i] 为i到根的距离。本题的方向可以无视。

实现一:Tarjin离线算法,复杂度为:O(N+Q) 219ms

代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
const int MAXN = 40010 ;
int N , M ,Q;
struct Node{
int num ,next ;
int dis ;
}edge[MAXN*2] ;
int root[MAXN] , cnt ;
int f[MAXN] ;
int dis[MAXN] ;
bool vis[MAXN] ;
int p[MAXN] ;

struct Node1{
int s,e ;
int next ;
int f  ;
}e2[10010*2] ;
int root2[MAXN] ,c2 ;
int ans[MAXN] ;

void add(int a, int b , int c){
edge[cnt].num = b;
edge[cnt].next = root[a] ;
edge[cnt].dis = c ;
root[a] = cnt++ ;
}
void Build(int u){
for(int i=root[u] ;i!=-1;i=edge[i].next){
int v = edge[i].num;
if(v == f[u])	continue ;
f[v] = u ;
Build(v) ;
}
}
void dfs(int u){
for(int i=root[u] ;i!=-1; i=edge[i].next){
int v = edge[i].num;
if(v == f[u])	continue ;
dis[v] = dis[u] + edge[i].dis ;
dfs(v) ;
}
}
void add2(int a ,int b ,int c){
e2[c2].e = b ;
e2[c2].s = a ;
e2[c2].next = root2[a] ;
e2[c2].f = c ;
root2[a] = c2++ ;
}
int find(int a){
if(a != p[a]){
p[a] = find( p[a] ) ;
}
return p[a] ;
}
void tarjin(int u){
vis[u] = 1;
for(int i=root2[u] ;i!=-1;i=e2[i].next){
int v = e2[i].e ;
int f = e2[i].f ;
if(vis[v]){
ans[f] = find(v) ;
}
}
for(int i=root[u] ;i!=-1;i=edge[i].next){
int v = edge[i].num ;
if(v == f[u])	continue ;
if(!vis[v]){
tarjin(v);
p[v] = u ;
}
}
}
int main(){
int a,b ,c ,ff;
char ch[4] ;
while(scanf("%d %d",&N,&M) == 2){
memset(root , -1, sizeof(root));
cnt = 0 ;
for(int i=1;i<=M;i++){
scanf("%d %d %d %s",&a,&b,&c,ch);
add(a,b,c) ;	add(b,a,c) ;
}
memset(f ,-1 ,sizeof(f)) ;
Build(1);
dis[1] = 0;
dfs(1) ;
memset(root2, -1 , sizeof(root2));
c2 = 0 ;
scanf("%d",&Q);
for(int i=1;i<=Q;i++){
scanf("%d %d",&a,&b);
add2(a,b,i);	add2(b,a,i) ;
}
memset(vis, 0 ,sizeof(vis));
for(int i=1;i<=N;i++)	p[i] = i ;
tarjin(1) ;
for(int i=0;i<c2;i+=2){
a = e2[i].s ;
b = e2[i].e ;
ff = e2[i].f ;
int res = dis[a] + dis[b] - 2 * dis[ ans[ff] ] ;
printf("%d\n",res);
}
}
return 0 ;
}


实现二:dfs +rmq ,复杂度:O( 2*N*log(2*N) ) + O(1) 375ms

代码:

/*
R[MAXN] : 每个结点第一次被遍历的下标
E[MAXN] : dfs序列
dep[MAXN] : dfs遍历结点深度
算法描述:
(1)DFS:从树T的根开始,进行深度优先遍历,并记录下每次到达的顶点。
第一个的结点是root(T),每经过一条边都记录它的端点。由于每条边
恰好经过2次,因此一共记录了2n-1个结点,用E[1, ... , 2n-1]来表示。
(2)计算R:用R[i]表示E数组中第一个值为i的元素下标,即如果R[u] < R[v]
时,DFS访问的顺序是E[R[u], R[u]+1, ..., R[v]]。虽然其中包含u的后
代,但深度最小的还是u与v的公共祖先。
(3)RMQ:当R[u] ≥ R[v]时,LCA[T, u, v] = RMQ(L, R[v], R[u]);否
则LCA[T, u, v] = RMQ(L, R[u], R[v]),计算RMQ。由于RMQ中使用的ST
算法是在线算法,所以这个算法也是在线算法。
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define MIN(a,b) (a)>(b)?(b):(a)
const int MAXN = 40000+10 ;
int N ,M , Q ;
struct Edge{ int num,next,dis ;}edge[MAXN*2] ;
int root[MAXN] , cnt ;
bool vis[MAXN] ;
int R[MAXN] , E[MAXN*2+1] ,dep[MAXN*2+1] ,idx;
int dp[2*MAXN+1][22] ;
int d[22] ,dis[MAXN];

void Init(){
memset(root , -1 ,sizeof(root));
memset( vis , 0,sizeof(vis) );
cnt = 0 ;
}
void add_edge(int a, int b ,int c){
edge[cnt].num = b ;
edge[cnt].next = root[a] ;
edge[cnt].dis = c ;
root[a] = cnt++ ;
}
void dfs(int u ,int d){
vis[u] = 1 ;
R[u] = idx ; E[idx] = u ; dep[idx++] = d ;
for(int i=root[u] ;i!=-1;i=edge[i].next){
int v = edge[i].num;
if(vis[v] == 0){
dfs(v, d+1);
E[idx] = u ; dep[idx++] = d ;
}
}
}
void cal(int u){
vis[u] = 1;
for(int i=root[u] ;i!=-1;i=edge[i].next){
int v= edge[i].num;
int d = edge[i].dis  ;
if(vis[v] == 0){
dis[v] = dis[u] + d ;
cal(v) ;
}
}
}
void Init_Rmq(){
int i , j ;
for(d[0]=1,i=1;i<22;i++)d[i]=d[i-1]*2 ;
for(i=1;i<idx;i++)	dp[i][0] = i ;

for(j=1;(1<<j)<idx;j++){
for(i=1;i+(1<<j)<idx;i++){
int idx1 = dp[i][j-1] ;
int idx2 = dp[i+(1<<(j-1))][j-1] ;
if(dep[ idx1 ] > dep[ idx2 ]){
dp[i][j] = idx2 ;
}
else{
dp[i][j] = idx1 ;
}
}
}
}
int main(){
int i,j,a,b,c;
char ch[5] ;
while(scanf("%d %d",&N,&M) == 2){
Init() ;
for(i=0;i<M;i++){
scanf("%d %d %d %s",&a,&b,&c,ch);
add_edge(a,b,c);	add_edge(b,a,c) ;
}
idx = 1 ;
memset(vis , 0, sizeof(vis));
dis[1] = 0 ;
cal(1);
memset(vis , 0, sizeof(vis));
dfs(1,0);
Init_Rmq() ;
scanf("%d",&Q);
for(i=0;i<Q;i++){
scanf("%d %d",&a,&b);
int fa = R[a] ;
int fb = R[b] ;
if(fa > fb){
std::swap(fa, fb) ;
}
int k = int(log(double(fb-fa+1)) / log(2.0)) ;
int idx1 = dp[ fa ][k] ;
int idx2 = dp[ fb-(1<<k)+1 ][k] ;
if(dep[ idx1 ] > dep[ idx2 ])	c = idx2 ;
else	c = idx1 ;
printf("%d\n",dis[a]+dis[b]-2*dis[ E[c] ]);
}
}
return 0 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: