【题目】
题目大意:给出一个有 n n n 个点, m m m 条边的无向图( m − n ≤ 20 m-n≤20 m−n≤20),保证图是连通的,边的权值为 d i d_i di,给出 q q q 组询问,每次给出两个点 s s s 和 t t t ,询问 s s s 到 t t t 的最短路
数据范围: 1 ≤ n , m ≤ 1 0 5 1≤n,m≤10^5 1≤n,m≤105, 1 ≤ d i ≤ 1 0 9 1≤d_i≤10^9 1≤di≤109, 1 ≤ q ≤ 1 0 5 1≤q≤10^5 1≤q≤105
【分析】
其实这道题的关键是 m − n ≤ 20 m-n≤20 m−n≤20
那我们先随便选一个点作为根建一棵树,那么用以下两种情况来讨论:
- 若 s s s 到 t t t 的最短路径不经过非树边,直接求出 l c a lca lca 就可以轻松得出答案
- 若 s s s 到 t t t 的最短路径经过非树边,就可以直接用非树边的两个端点来更新答案
由于非树边最多有 21 21 21 条,这样的特殊点最多有 42 42 42 个,直接预处理出这 42 42 42 个点到其它点的最短路就可以了
注意数据范围较大,要开 l o n g long long l o n g long long
【代码】
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 500005
#define inf 1ll<<50ll
using namespace std;
bool vis[N];
int n,m,q,t,cnt;
int first[N],w[M],v[M],nxt[M];
int dep[N],point[N],father[N][25];
long long len[N],d[50][N];
priority_queue<pair<int,int> >que;
void add(int x,int y,int z)
{
t++;
nxt[t]=first[x];
first[x]=t;
v[t]=y;
w[t]=z;
}
void dfs(int x)
{
int i,j;
vis[x]=true;
for(i=first[x];i;i=nxt[i])
{
j=v[i];
if(j==father[x][0]) continue;
if(!vis[j]) father[j][0]=x,dep[j]=dep[x]+1,len[j]=len[x]+w[i],dfs(j);
else point[++cnt]=x,point[++cnt]=j;
}
}
int lca(int x,int y)
{
int i;
if(dep[x]<dep[y]) swap(x,y);
for(i=20;~i;--i)
if(dep[father[x][i]]>=dep[y])
x=father[x][i];
if(x==y) return x;
for(i=20;~i;--i)
{
if(father[x][i]!=father[y][i])
{
x=father[x][i];
y=father[y][i];
}
}
return father[x][0];
}
void dijkstra(int id,int s)
{
int x,y,i;
while(!que.empty()) que.pop();
for(i=1;i<=n;++i) d[id][i]=inf;
d[id][s]=0;que.push(make_pair(0,s));
while(!que.empty())
{
x=que.top().second;
que.pop();
for(i=first[x];i;i=nxt[i])
{
y=v[i];
if(d[id][y]>d[id][x]+w[i])
{
d[id][y]=d[id][x]+w[i];
que.push(make_pair(-d[id][y],y));
}
}
}
}
int main()
{
int x,y,z,i,j,l;
long long ans;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
dep[1]=1,dfs(1);
sort(point+1,point+cnt+1);
l=unique(point+1,point+cnt+1)-(point+1);
for(i=1;i<=l;++i) dijkstra(i,point[i]);
for(j=1;j<=20;++j)
for(i=1;i<=n;++i)
father[i][j]=father[father[i][j-1]][j-1];
scanf("%d",&q);
for(i=1;i<=q;++i)
{
scanf("%d%d",&x,&y);
ans=len[x]+len[y]-2*len[lca(x,y)];
for(j=1;j<=l;++j) ans=min(ans,d[j][x]+d[j][y]);
printf("%lld\n",ans);
}
return 0;
}