传送门
problem
J 国有
n
n
n 座城市,这些城市之间通过
m
m
m 条单向道路相连,已知每条道路的长度。
一次,居住在 J 国的 Rainbow 邀请 Vani 来作客。不过,作为一名资深的旅行者,Vani 只对 J 国的
k
k
k 座历史悠久、自然风景独特的城市感兴趣。
请求出 Vani 感兴趣的城市之间「两两最短路」的最小值(即在他感兴趣的城市中,最近的一对的最短距离)。
数据范围:
n
≤
1
0
5
n≤10^5
n≤105,
m
≤
5
×
1
0
5
m≤5\times 10^5
m≤5×105,
2
≤
k
≤
n
2\le k\le n
2≤k≤n。
solution
这道题其实不难。
我们跑两次 dijkstra,第一次从所有关键点出发,每个点记录两个值:
d
i
s
1
[
i
]
dis_1[i]
dis1[i] 为
i
i
i 到最近的关键点的距离,
p
r
e
1
[
i
]
pre_1[i]
pre1[i] 为最近关键点的编号。第二次终点是关键点(相当于是在反图上以关键点为起点跑),同样记两个值:
d
i
s
2
[
i
]
dis_2[i]
dis2[i] 和
p
r
e
2
[
i
]
pre_2[i]
pre2[i]。
然后我们可以枚举边
(
u
,
v
)
(u,v)
(u,v),如果
p
r
e
1
[
u
]
≠
p
r
e
2
[
v
]
pre_1[u]\ne pre_2[v]
pre1[u]=pre2[v],则以
d
i
s
1
[
u
]
+
w
(
u
,
v
)
+
d
i
s
2
[
v
]
dis_1[u]+w(u,v)+dis_2[v]
dis1[u]+w(u,v)+dis2[v] 更新答案。
时间复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)。
code
#include<bits/stdc++.h>
#define N 100005
#define ll long long
using namespace std
;
typedef pair
<int,ll
>Pair
;
int n
,m
,k
,t
;
int a
[N
],tag
[N
],vis
[N
],pre1
[N
],pre2
[N
];
vector
<Pair
>G
[N
],rG
[N
];
ll dis1
[N
],dis2
[N
];
priority_queue
<Pair
>Q
;
void dijkstra(ll
*dis
,int *pre
){
memset(vis
,0,sizeof(vis
));
memset(dis
,0x3f,sizeof(ll
)*(n
+1));
for(int i
=1;i
<=k
;++i
)
dis
[a
[i
]]=0,pre
[a
[i
]]=a
[i
],Q
.push(Pair(0,a
[i
]));
while(!Q
.empty()){
int x
=Q
.top().second
;Q
.pop();
if(vis
[x
]) continue;vis
[x
]=1;
for(Pair
&now
:G
[x
]){
int to
=now
.first
;
if(dis
[to
]>dis
[x
]+now
.second
){
dis
[to
]=dis
[x
]+now
.second
;
pre
[to
]=pre
[x
],Q
.push(Pair(-dis
[to
],to
));
}
}
}
}
int main(){
int T
,u
,v
,w
;
scanf("%d",&T
);
while(T
--){
scanf("%d%d%d",&n
,&m
,&k
);
for(int i
=1;i
<=m
;++i
){
scanf("%d%d%d",&u
,&v
,&w
);
G
[u
].push_back(Pair(v
,w
)),rG
[v
].push_back(Pair(u
,w
));
}
for(int i
=1;i
<=k
;++i
) scanf("%d",&a
[i
]),tag
[a
[i
]]=1;
dijkstra(dis1
,pre1
);
for(int i
=1;i
<=n
;++i
) G
[i
]=rG
[i
];
dijkstra(dis2
,pre2
);
ll ans
=1e18;
for(int i
=1;i
<=n
;++i
)
for(Pair
&x
:G
[i
])
if(pre1
[x
.first
]&&pre2
[i
]&&pre1
[x
.first
]!=pre2
[i
])
ans
=min(ans
,dis1
[x
.first
]+dis2
[i
]+x
.second
);
printf("%lld\n",ans
);
for(int i
=1;i
<=n
;++i
) tag
[i
]=0,G
[i
].clear(),rG
[i
].clear(),pre1
[i
]=pre2
[i
]=0;
}
return 0;
}