题意:给出一棵树,求任意两点的最小距离。查询为多组。
思路:LCA(Least Common Ancestor最近公共祖先)。设LCA(X, Y) = L,dist(X)表示X到根节点的距离(Y同理),那么X到Y的路径长度就是dist(X) + dist(Y) - 2 * dist(L)。
离线方法为tarjan算法,本质是深搜+并查集。复杂度O(m+q),q为查询的对数。
在线方法为rmq,(具体算法参见http://ayzk.wordpress.com.cn/archives/14),复杂度为O(nlogn)+O(q).
tarjan算法:
#include <stdio.h> #include <string.h> #define N 10005 #define M 40005 struct edge{ int y,w,next; }e[M<<1]; struct query{ int y,index,next; }q[N<<1]; int res[N],firstq[M],firste[M],visited[M],root[M],dis2root[M]; int n,m,top_q,top_e; void init(){ int i; top_q = top_e = 0; memset(res,0,sizeof(res)); memset(firstq,-1,sizeof(firstq)); memset(firste,-1,sizeof(firste)); memset(visited,0,sizeof(visited)); memset(dis2root,0,sizeof(dis2root)); for(i = 0;i<=n;i++) root[i] = i; } void adde(int x,int y,int w){ e[top_e].y = y; e[top_e].w = w; e[top_e].next = firste[x]; firste[x] = top_e++; } void addq(int x,int y,int index){ q[top_q].y = y; q[top_q].index = index; q[top_q].next = firstq[x]; firstq[x] = top_q++; } int find(int x){ if(root[x] == x) return x; else return root[x] = find(root[x]); } void dfs(int x,int dis){ int j,y; visited[x] = 1; for(j = firste[x];j!=-1;j=e[j].next){ y = e[j].y; if(!visited[y]){ dis2root[y] = dis+e[j].w; dfs(y,dis+e[j].w); root[y] = x; } } for(j = firstq[x];j!=-1;j=q[j].next) if(visited[q[j].y]){ res[q[j].index] = dis2root[x]+dis2root[q[j].y]-2*dis2root[find(q[j].y)]; } } int main(){ freopen("a.txt","r",stdin); while(scanf("%d %d",&n,&m)!=EOF){ int i,j,a,b,w; char ch; init(); for(i = 0;i<m;i++){ scanf("%d %d %d %c",&a,&b,&w,&ch); adde(a,b,w); adde(b,a,w); } scanf("%d",&m); for(i = 0;i<m;i++){ scanf("%d %d",&a,&b); addq(a,b,i); addq(b,a,i); } dfs(1,0); for(i = 0;i<m;i++) printf("%d\n",res[i]); } return 0; }
rmq算法:
#include <stdio.h> #include <string.h> #include <math.h> #define min(a,b) ((a)<(b)?(a):(b)) #define clc(s,t) memset(s,t,sizeof(s)) #define swap(a,b,k) {k=a;a=b;b=k;} #define N 40005 struct edge{ int y,w,next; }e[N*2]; int first[N],top,n,m,q,dis[N]; int flag[N<<1],r[N],d[N<<1],dp[N<<1][20],len; void init(){ clc(first,-1); clc(r, 0); clc(dis, 0); top = len = 0; } void add(int x,int y,int w){ e[top].y = y; e[top].w = w; e[top].next = first[x]; first[x] = top++; } void dfs(int x,int fa,int dep){ int i; for(i = first[x];i!=-1;i=e[i].next) if(e[i].y != fa){ dis[e[i].y] = dis[x]+e[i].w; flag[++len] = e[i].y; r[e[i].y] = len; d[len] = dep+1; dfs(e[i].y,x,dep+1); flag[++len] = x; d[len] = dep; } } void st(int n){ int i,j; int k = log((double)(1+n))/log(2.0); for(i = 1;i<=n;i++) dp[i][0] = i; for(j = 1;j<=k;j++) for(i = 1;i+(1<<j)-1<=n;i++){ if(d[dp[i][j-1]] < d[dp[i+(1<<(j-1))][j-1]]) dp[i][j] = dp[i][j-1]; else dp[i][j] = dp[i+(1<<(j-1))][j-1]; } } int query(int a,int b){ int k = log((double)(b-a+1))/log(2.0); if(d[dp[a][k]] < d[dp[b-(1<<k)+1][k]]) return dp[a][k]; return dp[b-(1<<k)+1][k]; } int main(){ int i,a,b,w,lca; char ch; init(); scanf("%d %d",&n,&m); for(i = 0;i<m;i++){ scanf("%d %d %d %c",&a,&b,&w,&ch); add(a,b,w); add(b,a,w); } scanf("%d",&q); r[1] = flag[++len] = 1; dfs(1,-1,0); st(n*2-1); while(q--){ scanf("%d %d",&a,&b); if(r[a]>r[b]){ swap(a,b,w); } lca = flag[query(r[a], r[b])]; printf("%d\n",dis[a]+dis[b]-2*dis[lca]); } return 0; }