题意:给你一颗顶点数为n的树,然后再给出一条边,连接这棵树的两个顶点。然后有q条询问,每条询问给出两个点a、b,问从a-b走最后给出的边是否能节省路程,若能则输出节省的路程,若不能输出0。
这道题之前做过,今天看的时候发现用lca写起来会非常简单,唉, 当时还不会lca,要不就不会写的那么麻烦了……
这题将其简化,那么就是个求树上两点距离的问题。首先,询问给出的两个点u、v这两个点在树上的距离是ans1,这两个点通过给出的边所要走的距离是ans2,那么如果ans1>ans2,那么说明能节省路程,否则不能节省路程。ans1好算,用w[i]表示根节点到i的距离,那么只要计算出anc=lca(u,v),u-v的距离就为w[u]+w[v]-2*w[anc]。至于ans2,这个也好办,只要在这两条路径中选一个最短的就行:u-x-y-v,v-x-y-u,其中x、y是给出的边连接的两个顶点,x-y的距离就是该边的权值。
这么写的话虽然时间上比之前得方法要慢一些,但是写起来非常容易,思路也很清晰,不容易出错。
之前写的本本题题解:http://blog.csdn.net/qian99/article/details/8918058
代码:
#include <iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #define inf 0x3f3f3f3f #define Inf 0x3FFFFFFFFFFFFFFFLL #define eps 1e-9 #define pi acos(-1.0) using namespace std; const int maxn=100000+10; const int dep=17; struct Edge { int v,dist; }; vector<Edge>edges; vector<int>G[maxn]; int d[maxn],w[maxn],fa[maxn][dep+1]; bool vis[maxn]; void clearAll(int n) { for(int i=0;i<=n;++i) G[i].clear(); edges.clear(); memset(fa,0,sizeof(fa)); memset(vis,0,sizeof(vis)); } void AddEdges(int u,int v,int dist) { edges.push_back((Edge){v,dist}); edges.push_back((Edge){u,dist}); int m=edges.size(); G[u].push_back(m-2); G[v].push_back(m-1); } void bfs(int s,int n) { d[s]=0;w[s]=0; queue<int>q; q.push(s); Edge e; while(!q.empty()) { int u=q.front();q.pop(); if(vis[u]) continue; vis[u]=true; for(int i=0;i<G[u].size();++i) { e=edges[G[u][i]]; if(!vis[e.v]) { d[e.v]=d[u]+1; w[e.v]=w[u]+e.dist; fa[e.v][0]=u; q.push(e.v); } } } for(int i=1;i<=dep;++i) for(int j=1;j<=n;++j) fa[j][i]=fa[fa[j][i-1]][i-1]; } int lca(int x,int y) { if(x==y) return x; if(d[x]>d[y]) swap(x,y); for(int i=dep;i>=0;--i) if(d[fa[y][i]]>d[x]) y=fa[y][i]; if(fa[y][0]==x) return x; if(d[y]>d[x]) y=fa[y][0]; for(int i=dep;i>=0;--i) { if(fa[x][i]!=fa[y][i]) { x=fa[x][i]; y=fa[y][i]; } } return fa[x][0]; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int t,tcase=0; scanf("%d",&t); int n,q; while(t--) { tcase++; scanf("%d%d",&n,&q); clearAll(n); int x,y,z; for(int i=0;i<n-1;++i) { scanf("%d%d%d",&x,&y,&z); AddEdges(x,y,z); } bfs(1,n); scanf("%d%d%d",&x,&y,&z); printf("Case #%d:\n",tcase); int u,v,anc,anc2,ans1,ans2; while(q--) { scanf("%d%d",&u,&v); anc=lca(u,v); ans1=w[u]+w[v]-2*w[anc]; anc=lca(u,x); anc2=lca(v,y); ans2=w[u]+w[x]-2*w[anc]+w[v]+w[y]-2*w[anc2]+z; anc=lca(u,y); anc2=lca(v,x); ans2=min(ans2,w[u]+w[y]-2*w[anc]+w[v]+w[x]-2*w[anc2]+z); if(ans1>ans2) ans1=ans1-ans2; else ans1=0; printf("%d\n",ans1); } } return 0; }