100
题意:给出一棵树,求出树上某两点之间的(最短)距离。
LCA 模板题
离线算法:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int inf=0x7f7f7f7f; struct node { int next,en,len; }E[100000]; struct node1 { int next,en; }query[100000]; int num1,num2,head1[100000],head2[100000],fa[100000],dis[100000],ans[100000]; int n,m; bool vis[100000]; void init() { num1=num2=0;int i; memset(head1,-1,sizeof(head1)); memset(head2,-1,sizeof(head2)); memset(vis,false,sizeof(vis)); memset(dis,inf,sizeof(dis)); for(i=0;i<=n;i++) fa[i]=i; } void add1(int st,int en,int len) { E[num1].en=en;E[num1].len=len; E[num1].next=head1[st];head1[st]=num1++; } void add2(int st,int en) { query[num2].en=en;query[num2].next=head2[st];head2[st]=num2++; } int find(int x) { if(x==fa[x]) return fa[x]; else return fa[x]=find(fa[x]); } void dfs(int x) { vis[x]=true; int i; for(i=head2[x];i!=-1;i=query[i].next) { int v=query[i].en; if(vis[v]) ans[i>>1]=dis[x]+dis[v]-2*dis[find(v)]; } for(i=head1[x];i!=-1;i=E[i].next) { int v=E[i].en; if(!vis[v]) { dis[v]=dis[x]+E[i].len; dfs(v); fa[v]=x; } } } int main() { int T,i,j; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); init(); for(i=1;i<=n-1;i++) { int st,en,len; scanf("%d%d%d",&st,&en,&len); add1(st,en,len);add1(en,st,len); } for(i=0;i<m;i++) { int st,en; scanf("%d%d",&st,&en); add2(st,en);add2(en,st); } dis[1]=0; dfs(1); for(i=0;i<num2;i+=2) { printf("%d\n",ans[i>>1]); } } return 0; }
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; struct node { int en,next,len; }E[80010]; int num,head[50000]; int deep[80010],fa[80010],dis[50000],p[80010][20];//p[i][j]表示i结点的第2^j祖先,dis[i]表示到根的距离 int n,m; void init() { num=0;int i; memset(head,-1,sizeof(head)); } void add(int st,int en,int len) { E[num].en=en;E[num].len =len; E[num].next=head[st];head[st]=num++; } void dfs(int x,int father,int depth) { deep[x]=depth; fa[x]=father; int i ,v; for(i=head[x];i!=-1;i=E[i].next) { v=E[i].en; if(v!=father) { dis[v]=dis[x]+E[i].len; dfs(v,x,depth+1); } } } void rmq()//求最近公共祖先 { int i,j; memset(p,-1,sizeof(p)); for(i=1; i <= n; i++) p[i][0] = fa[i]; for(j=1; (1<<j) <= n; j++) { for(i=1; i <= n; i++) { if(p[i][j-1]!=-1) p[i][j]=p[p[i][j-1]][j-1];//i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先 } } } int query(int st,int en) { if(deep[st]<deep[en]) swap(st,en); int i,j; for(i=0;(1<<i)<=deep[st];i++); i--; //使st,en两点的深度相同 for(j=i;j>=0;j--) if(deep[st]-(1<<j)>=deep[en]) st=p[st][j]; if(st==en) return st; for(j=i;j>=0;j--)//倍增法,每次向上进深度2^j,找到最近公共祖先的子结点 { if(p[st][j]!=-1&&p[st][j]!=p[en][j]) { st=p[st][j];en=p[en][j]; } } return fa[st]; } int main() { int T,st,en,len,i,j; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); init(); for(i=1;i<n;i++) { scanf("%d%d%d",&st,&en,&len); add(st,en,len);add(en,st,len); } dis[1]=0;dfs(1,-1,0); rmq(); while(m--) { scanf("%d%d",&st,&en); int ans=query(st,en); printf("%d\n",dis[st]+dis[en]-2*dis[ans]); } } return 0; }