题目描述:
小雨所在的城市一共有 m 条地铁线,分别标号为 1 号线,2 号线,……,m 号线。
整个城市一共有 n 个车站,编号为1∼n 。其中坐 i 号线需要花费 ai的价格,每坐一站就需要多花费 bi的价格。
i 号线有 ci个车站,而且这 ci个车站都已知,如果某一站有多条地铁线经过,则可以在这一站换乘到另一条地铁线,并且能多次换乘。
现在小雨想从第 s 个车站坐地铁到第 t 个车站,地铁等待时间忽略不计,求最少花费的价格,若不能到达输出 -1 。
(地铁是双向的,所以 s 可能大于 t)

输入描述:
第一行输入四个正整数 n,m,s,t分别表示车站个数,地铁线数,起点站和终点站。
第二行到第 m + 1 行,每行前三个数为 ai,bi,ci,分别表示坐 i 号线的价格,i 号线每坐一站多花的价格,i 号线车站个数。
接下来 ci个数,表示 i 号线的每一个车站的编号,单调递增。

输出描述:
共一行,一个数表示最小花费,若不能到达输出 -1 。

思路
最短路分层图。
每一条路线都是一层,对于每条路线重复的点,暴力的话我们考虑m^2枚举每条路线,O(n)枚举点建边
这样的复杂度显然是不允许的,所以考虑建虚点。
虚点什么意思呢,顾名思义,并不是实际存在的点,而是人为创建的不存在的点,我们考虑建立一层虚点,把每条航线的每个点都连到虚点来,连到虚点的路径权值为0,把虚点到每条路线的每个点的路径权值为a,也就是乘坐的价钱。其实可以理解为在同一站点换乘另一路线,先到虚点这一层,这一层就是车站。这样建图复杂度只需要O(nm)
然后跑一次最短路。注意起点和终点位置应该在虚点的一层。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m,s,t;
struct dijkstra
{
    struct node
    {
        int to, cost;
        friend bool operator<(node a, node b)
        {
            return a.cost > b.cost;
        }
    };
    struct Edge
    {
        int to, next;
        int v;
    } edge[1 << 21];
    int head[1 << 20], tot;
    bool vis[1 << 20];
    ll d[1 << 20];
    void Init()
    {
        tot = 0;
        for (int i = 1; i <= 1000000; ++i)
            d[i] = -1, vis[i] = 0, head[i] = -1;
    }
    void add(int a, int b, int c)
    {
        edge[tot].to = b;
        edge[tot].v = c;
        edge[tot].next = head[a];
        head[a] = tot++;
    }
    void dj(int x)
    {
        priority_queue<node> q;
        q.push({x, 0});
        while (!q.empty())
        {
            node now = q.top();
            q.pop();
            if (!vis[now.to])
            {
                vis[now.to] = 1;
                d[now.to] = now.cost;
                for (int i = head[now.to]; i != -1; i = edge[i].next)
                {
                    node next;
                    next.to = edge[i].to;
                    next.cost = now.cost + edge[i].v;
                    if (!vis[next.to])
                        q.push(next);
                }
            }
        }
    }
} d;
int main()
{
    d.Init();///初始化
    cin>>n>>m>>s>>t;
    for(int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        int last;
        ///下面代码中 我的虚点建立在(m+1)*n与(m+2)*n之间
        for(int j=1;j<=c;j++){
            int x;cin>>x;
            if(j>1){///每一条路线相邻两个站点之间建边
                d.add(i*n+last,i*n+x,b);///上一个点到当前点
                d.add(i*n+x,i*n+last,b);///当前点到上一个点
            }
            d.add(i*n+x,(m+1)*n+x,0);///当前点到虚点
            d.add((m+1)*n+x,i*n+x,a);///虚点到当前点
            last=x;
        }
    }
    d.dj((m+1)*n+s);//起点取虚点那一层图
    ll mi=d.d[(m+1)*n+t];///终点取虚点那一层图
    cout<<mi;
    return 0;
}