题目大意:
给一张n个点,m条边的无向图。共有q个询问,每次询问u到v的最短路。
n <= 100000 , n-1 <= m <= n + 50 , q <= 50000
思路:
注意到边的的数量,n和m非常接近,所以可以近似于一棵树,这样,我们先抛出一棵最小生成树出来,然后之后这样肯定会有多出来的边,然后我们把多出来的边的端点都存下来,这样就出来了一些除了树上的点之外的点,这样我们再把这些点跑一边dij,这样之后我们对于查询u,v时不就变成了最小生成树上的距离和枚举每一个树上之外的点的dij的最小值了么,至于树上的最小值,我们可以lca,rmq搞;
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
#define fuck() cout<<"-----"<<endl
#define INF 0x3f3f3f3f
const int maxed=100000+10;
struct E
{
int v,bef,val;
}e[maxed*10][2];
struct Node
{
int u,val;
bool operator < (const Node& n1) const
{
return val>n1.val;
}
};
int n,m,q,ans,cnt,head[maxed][2],gen[maxed],dp[maxed],cost[maxed],l[100+10],d[100+10][maxed];
int f[maxed][20];
bool vis[maxed];
int main()
{
int slove1(int);
void add_(int,int,int,int);
void slove2(int,int);
void slove3(int,int);
void slove4();
int slove5(int,int);
int N,kcase=0;
scanf("%d",&N);
while(N--){
cnt=0;
ans=1;
memset(head,-1,sizeof(head));
memset(vis,false,sizeof(vis));
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
gen[i]=i;
int a,b,c;
for(int i=0;i<m;i++){
scanf("%d%d%d",&a,&b,&c);
add_(a,b,c,0);
add_(b,a,c,0);
int x=slove1(a),y=slove1(b);
if(x!=y){
add_(a,b,c,1);
add_(b,a,c,1);
gen[x]=y;
}
else{
if(!vis[a]){
l[++cnt]=a;
vis[a]=true;
}
if(!vis[b]){
l[++cnt]=b;
vis[b]=true;
}
}
}
//fuck();
memset(d,INF,sizeof(d));
for(int i=1;i<=cnt;i++)
slove2(l[i],i);
//fuck();
memset(vis,false,sizeof(vis));
memset(cost,0,sizeof(cost));
for(int i=1;i<=n;i++)
gen[i]=i;
slove3(1,1);
slove4();
printf("Case %d:\n",++kcase);
while(q--){
scanf("%d%d",&a,&b);
int w=slove5(a,b);
//cout<<w<<endl;
int answer=cost[a]+cost[b]-2*cost[w];
for(int i=1;i<=cnt;i++)
answer=min(answer,d[i][a]+d[i][b]);
printf("%d\n",answer);
}
}
return 0;
}
int slove1(int x)
{
if(x==gen[x])
return x;
return gen[x]=slove1(gen[x]);
}
void add_(int x,int y,int val,int cur)
{
e[ans][cur].v=y;
e[ans][cur].val=val;
e[ans][cur].bef=head[x][cur];
head[x][cur]=ans++;
}
void slove2(int s,int cur)
{
d[cur][s]=0;
memset(vis,false,sizeof(vis));
queue<Node> pq;
pq.push((Node){s,0});
while(!pq.empty()){
//fuck();
Node no=pq.front();
pq.pop();
for(int i=head[no.u][0];i!=-1;i=e[i][0].bef){
int w=d[cur][no.u]+e[i][0].val;
if(d[cur][e[i][0].v]>w){
d[cur][e[i][0].v]=w;
pq.push((Node){e[i][0].v,w});
}
}
}
}
void slove3(int x,int dep)
{
vis[x]=true;
dp[x]=dep;
for(int i=head[x][1];i!=-1;i=e[i][1].bef)
if(!vis[e[i][1].v]){
gen[e[i][1].v]=x;
cost[e[i][1].v]=cost[x]+e[i][1].val;
slove3(e[i][1].v,dep+1);
}
}
void slove4()
{
memset(f,-1,sizeof(f));
for(int i=1;i<=n;i++)
f[i][0]=gen[i];
int len=(int)log2(n);
for(int i=1;i<=len;i++)
for(int j=1;j<=n;j++){
int r=f[j][i-1];
if(r==-1)
continue;
f[j][i]=f[r][i-1];
}
}
int slove5(int x,int y)
{
if(dp[x]<dp[y])
swap(x,y);
int len=(int)log2(n);
for(int i=len;i>=0;i--){
int r=f[x][i];
if(r==-1||dp[r]<dp[y])
continue;
x=r;
}
if(x==y)
return x;
for(int i=len;i>=0;i--){
int r1=f[x][i],r2=f[y][i];
if(r1==r2)
continue;
x=r1;
y=r2;
//fuck();
}
//cout<<f[x][0]<<endl;
return f[x][0];
}