[最短路]D. 百度地图导航

百度地图上有 nn 个城市,城市编号依次为 11 到 nn。地图中有若干个城市群,编号依次为 11 到 mm。每个城市群包含一个或多个城市;每个城市可能属于多个城市群,也可能不属于任何城市群。

地图中有两类道路。第一类道路是 城市之间的快速路,两个城市 u,vu,v 之间增加一条距离为 cc 的边;第二类道路是城市群之间的高速路,连接两个城市群 a,ba,b,通过这条高速路,城市群 aa 里的每个城市与城市群 bb 里的每个城市之间两两增加一条距离为 cc 的边。图中所有边均为无向边。

你需要计算从城市 ss 到城市 tt 的最短路。

输入格式

第一行输入 n(1 \le n \le 20000),n(1n20000), m(0 \le m \le 20000)m(0m20000),分别表示城市总数和城市群总数。

接下来一共输入 mm 行。

第 ii 行首先输入一个 k_i(1 \le k_i \le n)ki(1kin),表示第 ii 个城市群中的城市数为 k_iki。接下来输入 k_iki 个数,表示第 ii 个城市群中每个城市的编号(保证一个城市群内的城市编号不重复且合法,\sum_{i=1}^{m}k_i \le 20000i=1mki20000)。

下一行输入一个整数 m_1(0 \le m_1 \le 20000)m1(0m120000),表示有 m_1m1 条第一类道路,即 城市之间的快速路

接下来 m_1m1 行,每行输入三个整数 u_i,v_i(1 \le u_i, v_i \le n),c_i(1 \le c_i \le 10^6)ui,vi(1ui,vin),ci(1ci106),分别表示快速路连接的两个城市编号和边的距离。

下一行输入一个整数 m_2(0 \le m_2 \le 20000)m2(0m220000),表示有 m_2m2 条第二类道路,即 城市群之间的高速路

接下来 m_2m2 行,每行输入三个整数 a_i,b_i(1 \le a_i, b_i \le m),l_i(1 \le l_i \le 10^6)ai,bi(1ai,bim),li(1li106),分别表示快速路连接的两个城市群编号和边的距离。

最后一行输入 s, t(1 \le s, t \le n)s,t(1s,tn),表示起点和终点城市编号。

输出格式

输出一个整数,表示城市 ss 到城市 tt 到最短路。如果不存在路径,则输出-1

样例说明

1 -> 2 - > 5或者1 -> 4 -> 5是最短的路径,总长度为 1212

样例输入
5 4
2 5 1
2 2 4
1 3
2 3 4
2
1 2 9
1 5 18
2
1 2 6
1 3 10
1 5
样例输出
12
/*我好难过啊*/
#include <bits/stdc++.h>
#define INF 1e17
using namespace std;
typedef long long LL;
const long long maxn=60010;
struct node{
    LL to,next,w;
}e[maxn*80];
LL head[maxn],cnt,vis[maxn],dis[maxn],n;
void Init(){
    memset(head,-1,sizeof(head));
    cnt=0;
}
void add(LL u,LL v,LL w){
    e[cnt].to=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
void spfa(LL x){
    memset(vis,0,sizeof(vis));
    for(LL i=1;i<=n*3;i++)
        dis[i]=INF;
    vis[x]=1;
    dis[x]=0;
    queue<LL>q;
    q.push(x);
    while(!q.empty()){
        LL u=q.front();q.pop();
        vis[u]=0;
        for(LL i=head[u];~i;i=e[i].next){
            LL v=e[i].to,w=e[i].w;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
int main(){
    LL m,x,u,v,w,m1,m2,k,s,t;
    Init();
    scanf("%lld%lld",&n,&m);
    for(LL i=1;i<=m;i++){/*拆点*/
        scanf("%lld",&k);
        while(k--){
            scanf("%lld",&x);
            add(x,n+i,0);
            add(n+i+n,x,0);
        }
    }
    scanf("%lld",&m1);
    while(m1--){
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    scanf("%lld",&m2);
    while(m2--){
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u+n,v+n+n,w);
        add(v+n,u+n+n,w);
    }
    scanf("%lld%lld",&s,&t);
    spfa(s);
    if(dis[t]==INF)
        printf("-1\n");
    else printf("%lld\n",dis[t]);
}
/*
5 2
3 1 2 3
2 4 5
1
1 2 5
1
1 2 1
1 2
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值