#include <cstdio> #include <cstring> #include <cmath> #include <vector> #include <iostream> #include <algorithm> using namespace std; const int maxn=1e4+10; const int maxc=1e6+10; struct node{ int to,w; node(int a=0,int b=0){to=a;w=b;} }; int f[maxn],dis[maxn],n,ans[maxc],vis[maxn],mark[maxn]; //f[i]并查集所用,记录前继结点 //dis[i]记录个点到跟结点的距离 //ans记录m个询问的答案 //vis标记查询过了的点 //mark记录已访问过的根节点。由于已遍历的树所有结点的find都是根节点,这样就能判断下是否在别的树里了 vector<node>e[maxn];//记录树 vector<node>q[maxn];//记录所求最短距离的两点 int find(int x) { if(x!=f[x])f[x]=find(f[x]); return f[x]; } void lca(int u) { int i,j,k,v,c; for(j=0;j<q[u].size();j++) { c=q[u][j].to; //如果所求两点中的对应点已知,则必定在同一子树上,由于并查集只记录所在子树。所以find(c),就是最近公共祖先 if(vis[c]&&ans[q[u][j].w]==-1&&mark[find(c)]!=1) { //cout<<f[c]<<"***"<<endl; ans[q[u][j].w]=dis[u]+dis[c]-2*dis[find(c)]; } } for(i=0;i<e[u].size();i++) { v=e[u][i].to; if(vis[v])continue; vis[v]=1; dis[v]=dis[u]+e[u][i].w; lca(v);//深度优先搜索 f[v]=u; } } int main() { int n,m,c; while(scanf("%d%d%d",&n,&m,&c)!=EOF) { int i,j,k,x,y,z; for(i=1;i<=n;i++) { e[i].clear(); q[i].clear(); f[i]=i; vis[i]=mark[i]=0; } memset(ans,-1,sizeof(ans)); for(i=0;i<m;i++) { scanf("%d%d%d",&x,&y,&z); e[x].push_back(node(y,z)); e[y].push_back(node(x,z)); } for(i=1;i<=c;i++) { scanf("%d%d",&x,&y); // if(x==y){ans[i]=0;continue;} q[x].push_back(node(y,i)); q[y].push_back(node(x,i)); } for(i=1;i<=n;i++) { if(!vis[i]) { vis[i]=1; dis[i]=0; lca(i); mark[i]=1; } } for(i=1;i<=c;i++) { if(ans[i]!=-1) printf("%d\n",ans[i]); else printf("Not connected\n"); } } return 0; } /* 最近公共祖先lca 离线算法/Tarjan算法,用mark标记是否在同一组里。 方法举例说明: 1 / \ 2 3 / \ 4 5 / / 7 8 / 9 查询(4,9):到4时,由于vis[9]=0,所以继续;到9后,最近公共祖先就是f[4]=4(回溯的时候记录父节点); 查询(9,8):深度优先搜索,所以到8时才询问;要到8必须回溯到2,在进到8这个子树,所以以记录f[9]=7;f[7]=4;f[4]=2;f[2]=2;所以find(2)=2; 查询(8,3):跟上条相似,必须回溯1才能到3,而find(8)=1就是最近公共祖先; 我们可以发现,对于查询的两点,都要在先查询到的点开始,回溯到最近公共祖先,才查询相对应的点。这也就是离线算法的思路了。。这是我自己理解的。。 专业解释请百度。。 */