题意:给你n个城市和m条边,并将这n个城市分为三类—L,R,M,其中在L类的城市必须用左手;在R类的城市必须用右手;在M类的城市不限制左右手。换一次手需要x的时间。问从起点到终点的最短路径。
思路:拆点的思想,将每个城市划分为L,R两类点,若边的两个端点类型一个为L,一个为R,需在原边权基础上加x,若为同类型则不需要加x。另外,再设一个源点和汇点,将源点和起点相连,边权为0.将汇点和终点相连,边权为0。连线规则:城市为M,则需要连它的L和R。而对于L,R只需要连对应的类型即可。连完跑一下最短路就可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e5+5;
const ll inf=99999999999999999;
struct Edge{
ll to,w,prev;
}edge[N*8];
ll T,n,m,s,t,x,cnt,head[N*4],v[N*4],dis[N*4];
string k;
inline void add(ll xx,ll yy,ll ww){
edge[++cnt].to=yy;edge[cnt].w=ww;
edge[cnt].prev=head[xx];head[xx]=cnt;
edge[++cnt].to=xx;edge[cnt].w=ww;
edge[cnt].prev=head[yy];head[yy]=cnt;
}
inline void dijkstra(){
priority_queue<pair<ll,ll> > q;
dis[0]=0;q.push({
0,0});
while(!q.empty()){
ll xx=q.top().second;q.pop();
if(v[xx]) continue;
v[xx]=1;
for(ll i=head[xx];i;i=edge[i].prev){
ll to=edge[i].to;
if(dis[to]>dis[xx]+edge[i].w){
dis[to]=dis[xx]+edge[i].w;
q.push({
-dis[to],to});
}
}
}
cout<<dis[2*n+1]<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--){
cin>>n>>m>>s>>t>>x>>k;
for(ll i=0;i<=2*n+1;i++) dis[i]=inf,v[i]=head[i]=0;
ll u,v,w;cnt=0;
if(k[s-1]=='L') add(0,s,0);
else if(k[s-1]=='R') add(0,s+n,0);
else add(0,s,0),add(0,s+n,0);
if(k[t-1]=='L') add(t,2*n+1,0);
else if(k[t-1]=='R') add(t+n,2*n+1,0);
else add(t,2*n+1,0),add(t+n,2*n+1,0);
for(ll i=1;i<=m;i++){
cin>>u>>v>>w;
if(k[u-1]=='L'&&k[v-1]=='R') add(u,v+n,w+x);
else if(k[u-1]=='R'&&k[v-1]=='L') add(u+n,v,w+x);
else if(k[u-1]=='L'&&k[v-1]=='M') add(u,v,w),add(u,v+n,w+x);
else if(k[u-1]=='M'&&k[v-1]=='L') add(u,v,w),add(u+n,v,w+x);
else if(k[u-1]=='R'&&k[v-1]=='M') add(u+n,v,w+x),add(u+n,v+n,w);
else if(k[u-1]=='M'&&k[v-1]=='R') add(u,v+n,w+x),add(u+n,v+n,w);
else if(k[u-1]=='L'&&k[v-1]=='L') add(u,v,w);
else if(k[u-1]=='R'&&k[v-1]=='R') add(u+n,v+n,w);
else add(u,v,w),add(u,v+n,w+x),add(u+n,v,w+x),add(u+n,v+n,w);
}
dijkstra();
}
return 0;
}