题目大意
给定一个n个点,m条边的图,每个边最多仅在一个环中
有q个询问,每次询问给出任意两点x和y,输出x和y的最小距离。
数据范围n,q<=10000,m<=12000,边权<=32768。
每个边最多仅在一个环中——仙人掌
为了方便,我们先对原图进行一次spfa,设di[1]=0,算出其他的di。
然后建一个新图,每个环中,除顶点(原深度最小的点)外,都向顶点连一条权值为0的边,记录环的总长,同时删除环中的原边。
新图一定是棵树,于是我们就可以倍增求lca。
对于每个询问x,y
我们记p为原x,q为原y,然后x和y都跳到它们的lca的儿子位置。
若x,y不在同一环内,答案显然为di[p]+di[q]-2*di[lca]。
否则,我们可以算出x和y的距离,再加上p到x的距离和q到y的距离即可。
代码
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
using namespace std;
const int maxn=6*10000+5;
int k[maxn],next[maxn],g[maxn],c[maxn],w[maxn],dfn[maxn];
int h[maxn],len[maxn],f[maxn][15],d[maxn],qu[maxn],be[maxn];
ll di[maxn],s[maxn];
bool bz[maxn],bk[maxn];
int i,j,num,cnt,n,m,q,t;
void read(int &n){
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
n=0;while (ch>='0'&&ch<='9') n=n*10+ch-'0',ch=getchar();
}
void add(int x,int y,int t,int z){
next[++num]=k[x],k[x]=num;
g[num]=y,c[num]=t,w[num]=num+z;
}
void make(int x,int y,int l){
bz[l]=bz[w[l]]=1;i=y;len[++cnt]=c[l];
while (i!=x){
h[i]=cnt;bz[be[i]]=bz[w[be[i]]]=1;add(x,i,0,0);
len[cnt]+=c[be[i]],i=g[w[be[i]]];
}
}
void dfs(int x){
dfn[x]=++t;int i=k[x];
while (i>0){
if (!bz[i]){
bz[w[i]]=1;
if (dfn[g[i]]==0) be[g[i]]=i,s[g[i]]=s[x]+c[i],dfs(g[i]);
if (dfn[g[i]]<dfn[x]) make(g[i],x,i);
}i=next[i];
}
}
void df(int x){
int i=k[x];
while (i>0){
if (!bz[i]){
f[g[i]][0]=x,d[g[i]]=d[x]+1,df(g[i]);
}i=next[i];
}
}
ll lca(int x,int y){
if(d[x]<d[y]) swap(x,y);
int i,p=x,q=y;
fod(i,14,0) if(d[f[x][i]]>=d[y]) x=f[x][i];
if(x==y) return di[p]-di[q];
fod(i,14,0) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
if(h[x]&&h[x]==h[y])
{
ll i=abs(s[x]-s[y]);
return di[p]-di[x]+di[q]-di[y]+min(i,len[h[x]]-i);
}
else return di[p]+di[q]-2*di[f[x][0]];
}
void spfa(){
memset(di,63,sizeof(di));
di[1]=0;qu[1]=bk[1]=1;
int j=1,i=0;
while (i<j){
int x=qu[++i];bk[x]=0;
int l=k[x];
while (l>0){
int y=g[l];
if (di[y]>di[x]+c[l]){
di[y]=di[x]+c[l];
if (bk[y]==0) qu[++j]=y,bk[y]=1;
}
l=next[l];
}
}
}
int main(){
read(n);read(m);read(q);
fo(i,1,m){
int x,y,t;read(x);read(y);read(t);
add(x,y,t,1);add(y,x,t,-1);
}spfa();dfs(1);d[1]=1;df(1);
fo(j,1,14)fo(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
fo(i,1,q){
int x,y;read(x);read(y);
printf("%lld\n",lca(x,y));
}
}