2125: 最短路
Time Limit: 1 Sec Memory Limit: 259 MBSubmit: 616 Solved: 263
[ Submit][ Status][ Discuss]
Description
给一个N个点M条边的连通无向图,满足每条边最多属于一个环,有Q组询问,每次询问两点之间的最短路径。
Input
输入的第一行包含三个整数,分别表示N和M和Q 下接M行,每行三个整数v,u,w表示一条无向边v-u,长度为w 最后Q行,每行两个整数v,u表示一组询问
Output
输出Q行,每行一个整数表示询问的答案
Sample Input
1 2 1
1 4 1
3 4 1
2 3 1
3 7 1
7 8 2
7 9 2
1 5 3
1 6 4
5 6 1
1 9
5 7
Sample Output
6
HINT
对于100%的数据,N<=10000,Q<=10000
Source
仙人掌图上的最短路
如果题给图是一棵树,用倍增很容易解决
现在是仙人掌,,不过也能用类似倍增的做法
把原图略加修改
对于每个环,将dfs时第一个遍历到的点作为这个环的根,
环上其它点到这个点连边,边权为这个点到作为根的这个点的最短路
找环的话用tarjan缩起来,,
每个点维护dis为走到1号点的距离
最后分类讨论,两个点在这棵新树上的位置关系
一般情况下,最短路为dis[x] - dis[y] - 2*dis[lca(x,y)]
如果两点的LCA为某个作为某环根的点,那么这两个点一起走的时候可能会出现在同一个环
此时设x到lca路径在该环上的点为fx,同理fy
ans = dis[x] + dis[y] - dis[fx] - dis[fy] + Dis(fx,fy)
其中Dis(i,j)为同一环上两点的最短路
这个东西,一开始找环时处理每个点到根那个点,只沿着一个方向走的距离
计算就很方便了
。。第一次写调了有点久
首先要注意,每条边只属于一个环,这东西等价于是只属于一个点双连通分量。。
所以tarjan最好不要以点为主来找环,因为一个点可以同时属于多个点双
于是,,网上参考了别人的代码,以边为主搞tarjan。。这样就好写多了
注意LCA的时候每次fx,fy都要清零,因为有xy刚好是自上而下一条链的情况--
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn = 2E5 + 10;
struct E{
int x,y,w; E(){}
E(int x,int y,int w): x(x),y(y),w(w){}
};
struct E2{
int to,w; E2(){}
E2(int to,int w): to(to),w(w){}
};
int n,m,q,dfs_clock,fx,fy,scc,dis[maxn],bel[maxn],dfn[maxn]
,low[maxn],dist[maxn],tot[maxn],fa[maxn][15],L[maxn];
bool Mark[maxn];
vector <E2> v[maxn];
vector <E2> v2[maxn];
stack <E> s;
queue <int> Q;
void Build_scc(int A,int B)
{
++scc; Mark[A] = 1;
while (s.top().x != A && s.top().y != B) {
E e = s.top(); s.pop();
bel[e.x] = scc; tot[scc] += e.w;
dist[e.x] = dist[e.y] + e.w;
Q.push(e.x);
}
E e = s.top(); s.pop(); tot[scc] += e.w;
if (Q.empty()) v2[A].push_back(E2(B,tot[scc]));
while (!Q.empty()) {
int k = Q.front(); Q.pop();
int w = min(dist[k],tot[scc] - dist[k]);
v2[A].push_back(E2(k,w));
}
}
void Dfs1(int x,int from)
{
dfn[x] = low[x] = ++dfs_clock;
for (int i = 0; i < v[x].size(); i++) {
E2 e = v[x][i];
if (!dfn[e.to]) {
s.push(E(x,e.to,e.w));
Dfs1(e.to,x);
if (dfn[x] <= low[e.to]) Build_scc(x,e.to);
low[x] = min(low[x],low[e.to]);
}
else if (e.to != from && dfn[e.to] < low[x]) low[x] = dfn[e.to],s.push(E(x,e.to,e.w));
}
}
void Dfs2(int x,int from)
{
for (int i = 1; i < 15; i++) fa[x][i] = fa[fa[x][i-1]][i-1];
for (int i = 0; i < v2[x].size(); i++) {
E2 e = v2[x][i];
L[e.to] = L[x] + 1; fa[e.to][0] = x;
dis[e.to] = dis[x] + e.w;
Dfs2(e.to,x);
}
}
int LCA(int p,int q)
{
fx = fy = 0;
if (L[p] < L[q]) swap(p,q);
for (int j = 14; j >= 0; j--)
if (L[p] - (1<<j) >= L[q])
p = fa[p][j];
if (p == q) return p;
for (int j = 14; j >= 0; j--)
if (fa[p][j] != fa[q][j])
p = fa[p][j],q = fa[q][j];
fx = p; fy = q;
return fa[p][0];
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
cin >> n >> m >> q;
while (m--) {
int x,y,w; scanf("%d%d%d",&x,&y,&w);
v[x].push_back(E2(y,w));
v[y].push_back(E2(x,w));
}
Dfs1(1,0);
L[1] = 1; Dfs2(1,0);
while (q--) {
int x,y; scanf("%d%d",&x,&y);
int lca = LCA(x,y);
if (Mark[lca] && bel[fx] == bel[fy] && bel[fx]) {
int G = abs(dist[fx] - dist[fy]);
G = min(G,tot[bel[fx]] - G);
printf("%d\n",dis[x] + dis[y] - dis[fx] - dis[fy] + G);
}
else printf("%d\n",dis[x] + dis[y] - 2*dis[lca]);
}
return 0;
}