题意:n个点m条边,m-n≤20,给你q组询问,每组询问两个点之间的最短路径
分析:如果是n个点,n-1条边,显然lca就能完美解决
如果加一条边,显然需要把通过该边的路径与lca结果取最小值
但如何求通过该边的路径的最小值呢?
可以转化成通过该边两端点的路径的最小值
而通过端点的路径的最小值可以以端点跑个spfa或dij,求出其到所有节点的距离
那么从x到y经过点p的最短路径就可以表示为dis[p][x]+dis[p][y]
当然,这里需要跑spfa的点最多只有42个,也就是在树的基础上加了42条边
只需要将其取个最小值即可
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; #define ll long long const int maxm=2e5+1; const int maxp=3e1+1; const int maxn=1e5+1; const int maxlg=2e1+1; struct Node { int to,next,val; }e[maxm<<1]; struct edge { int x,y,z; }a[maxm]; int head[maxn]; ll dis[maxp<<1][maxn]; bool vis[maxn]; int deep[maxn]; bool isp_e[maxm]; bool isp_d[maxn]; int fa[maxn][maxlg]; ll val[maxn][maxlg]; int pp[maxp<<1]; int cnt,n,m; void add(int x,int y,int z) { e[++cnt].val=z; e[cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; } void spfa(int x,int p) { deque<int> q;q.push_back(x),dis[p][x]=0ll; while(!q.empty()) { int now=q.front();q.pop_front(); vis[now]=0; for(int i=head[now];i;i=e[i].next) { int v=e[i].to; if(dis[p][v]>dis[p][now]+(ll)e[i].val) { dis[p][v]=dis[p][now]+(ll)e[i].val; if(!vis[v]) { vis[v]=1; if(!q.empty()&&dis[p][v]<dis[p][q.front()]) q.push_front(v); else q.push_back(v); } } } } } void lca() { for(int i=1;i<maxlg;i++) for(int j=1;j<=n;j++) { int now=fa[j][i-1]; fa[j][i]=fa[now][i-1]; val[j][i]=val[j][i-1]+val[now][i-1]; } } ll ask(int x,int y) { if(deep[x]<deep[y]) swap(x,y); int now=deep[x]-deep[y]; ll ans=0; for(int i=0;now;i++,now>>=1) if(now&1) ans+=val[x][i],x=fa[x][i]; if(x==y) return ans; for(int i=maxlg-1;i>=0;i--) { if(fa[x][i]!=fa[y][i]) { ans+=val[x][i]+val[y][i]; x=fa[x][i],y=fa[y][i]; } } return ans+val[x][0]+val[y][0]; } void dfs(int x,int fax) { vis[x]=1; for(int i=head[x];i;i=e[i].next) { int v=e[i].to; if(!vis[v]) { fa[v][0]=x; val[v][0]=(ll)e[i].val; deep[v]=deep[x]+1; isp_e[i-1>>1]=1; dfs(v,x); } } } int main() { int q,x,y,z; memset(dis,0x3f,sizeof(dis)); scanf("%d%d",&n,&m); for(int i=0;i<m;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,z); a[i].x=x,a[i].y=y,a[i].z=z; } dfs(1,-1); memset(vis,0,sizeof(vis)); for(int i=0;i<m;i++) if(!isp_e[i]) isp_d[a[i].x]=isp_d[a[i].y]=1; int pnum=0; for(int i=1;i<=n;i++) if(isp_d[i]) pp[++pnum]=i,spfa(i,pnum); lca(); scanf("%d",&q); while(q--) { scanf("%d%d",&x,&y); ll ans=ask(x,y); for(int i=1;i<=pnum;i++) ans=min(ans,dis[i][x]+dis[i][y]); printf("%lld ",ans); } return 0; }