题目
题意
有一张 n n n 个点 m m m 条边的无向图,有 q q q 个询问,输出 u u u 到 v v v 的最短路长度。
思路
首先, n 、 m 、 q n、m、q n、m、q 都 < = 1 e 5 <=1e5 <=1e5。直接每次跑最短路是肯定要 T L E \tt TLE TLE 的。然而,我们需要注意数据的特性: m − n < = 20 m-n<=20 m−n<=20。也就是说,我们可以把这张图看为:首先有一棵树,然后在树上加 < = 20 <=20 <=20 条边。
我们考虑,若只是一棵树,直接 L C A \tt LCA LCA 就OK了。所以先做一次最小生成树,得到一棵树。这个时候,我们考虑加边:那么这时候就很简单了:若是求 u u u 和 v v v 之间的最短路,要么从树上走,要么通过加入的边走。那么我们只需要将加入的边 f r o m from from 和 t o to to 跑一下到每个点的最短路。最后得到 < = 40 <=40 <=40 个端点,与树上最短路径比较一下即可。
code
struct node
{
LL v, w;
node(){}
node(LL V,LL W)
{
v = V;
w = W;
}
friend bool operator <(node A,node B)
{
return A.w > B.w;
}
};
vector<node> NG[MAXN], G[MAXN];
struct Edge
{
LL u, v, w, Index;
Edge(){}
Edge(LL U,LL V,LL W,LL INDEX)
{
u = U;
v = V;
w = W;
Index = INDEX;
}
friend bool operator <(Edge A,Edge B)
{
return A.w < B.w;
}
}Eg[MAXN], Ks[MAXN];
bool Used[MAXN];
LL n, m, S, T, Fa[MAXN];
inline void MakeSet(LL x)
{
for (Int i = 1; i <= x; ++ i)
Fa[i] = i;
}
LL FindSet(LL x)
{
if (Fa[x] != x)
Fa[x] = FindSet( Fa[x] );
return Fa[x];
}
inline void Kruscal()
{
LL MST = 0;
MakeSet( n );
for (Int i = 1; i <= m && MST < n - 1; ++ i)
{
LL Fu = FindSet( Ks[i].u );
LL Fv = FindSet( Ks[i].v );
if (Fu == Fv)
continue;
NG[Ks[i].u].push_back(node(Ks[i].v, Ks[i].w));
NG[Ks[i].v].push_back(node(Ks[i].u, Ks[i].w));
Fa[Fu] = Fv;
MST ++;
Used[Ks[i].Index] = 1;
}
}
LL Dep[MAXN], Depp[MAXN], f[MAXN][MAXK];
void Build(LL x,LL Pre,LL Bq)
{
Dep[x] = Dep[Pre] + 1;
Depp[x] = Depp[Pre] + Bq;
f[x][0] = Pre;
for (Int i = 1; i < MAXK; ++ i)
f[x][i] = f[f[x][i - 1]][i - 1];
int l = NG[x].size();
for (Int i = 0; i < l; ++ i)
{
LL to = NG[x][i].v;
if (to == Pre)
continue;
Build(to, x, NG[x][i].w);
}
}
inline void Adjust(LL &u,LL Tar)
{
for (Int i = 19; i >= 0; -- i)
if (Dep[f[u][i]] >= Tar)
u = f[u][i];
}
inline LL LCA(LL u,LL v)
{
if (Dep[u] > Dep[v])
Adjust(u, Dep[v]);
else if (Dep[v] > Dep[u])
Adjust(v, Dep[u]);
if (u == v)
return (u + v) / 2;
for (Int i = 19; i >= 0; -- i)
if (f[u][i] != f[v][i])
u = f[u][i], v = f[v][i];
return f[u][0];
}
bool Pao[MAXN];
LL Dis[MAXM][MAXN];
priority_queue<node> Q;
inline void Dijkstra(LL Now,LL Bh)
{
Q.push(node(Now, 0));
Dis[Bh][Now] = 0;
while (! Q.empty())
{
node Pop = Q.top(); Q.pop();
LL x = Pop.v;
int l = G[Pop.v].size();
for (Int i = 0; i < l; ++ i)
{
LL to = G[Pop.v][i].v, Val = G[Pop.v][i].w;
if (Dis[Bh][x] + Val < Dis[Bh][to])
{
Dis[Bh][to] = Dis[Bh][x] + Val;
Q.push(node(to, Dis[Bh][to]));
}
}
}
}
LL Had[MAXN], tot;
map<LL, LL> Map;
map<LL, LL>::iterator item;
int main()
{
read( n ); read( m );
for (Int i = 1; i <= m; ++ i)
{
LL u, v, w;
read( u ); read( v ); read( w );
G[u].push_back(node(v, w));
G[v].push_back(node(u, w));
Ks[i] = Eg[i] = Edge(u, v, w, i);
}
sort(Ks + 1, Ks + 1 + m);
Kruscal();
Build(1, 0, 0);
for (Int i = 1; i <= m; ++ i)
if (! Used[i])
{
if (! Map[Eg[i].u])
{
Map[Eg[i].u] = ++ tot;
for (Int j = 1; j <= n; ++ j)
Dis[Map[Eg[i].u]][j] = INF;
Dijkstra(Eg[i].u, Map[Eg[i].u]);
}
if (! Map[Eg[i].v])
{
Map[Eg[i].v] = ++ tot;
for (Int j = 1; j <= n; ++ j)
Dis[Map[Eg[i].v]][j] = INF;
Dijkstra(Eg[i].v, Map[Eg[i].v]);
}
}
LL Q;
read( Q );
while (Q --)
{
LL u, v;
read( u ); read( v );
LL Mid = LCA(u, v);
LL Ans = Depp[u] + Depp[v] - Depp[Mid] * 2;
for (Int i = 1; i <= tot; ++ i)
Ans = Min(Ans, Dis[i][u] + Dis[i][v]);
printf("%lld\n", Ans);
}
return 0;
}