题目链接:​​百度地图导航​

题目大意:有n个点,m层城市群,每个城市群有一些城市,然后是一些道路,有的是城市之间的最短路,有的是城市群之间的最短路(城市群间的最短路代表两个城市群之间的城市之间都可以两两通过城市群之间的最短路到达),然后给出城市群的城市有哪些,给出城市之间的最短路和城市群之间的最短路

题目思路:n和m都是2e4,直接暴力做最短路当然不行,这个时候我们可以考虑对每个城市群进行拆点,然后做一遍最短路就好,拆点的时候还是尽量拆成两个点吧,n+i∗2−1和 n+i∗2这两个点在连接的时候add(n+i*2-1,n+j*2,cost)和add(n+j*2-1,n+i*2,cost),这样去建边就可以保证双向边建立成功了,不然只拆一个点容易造成单向边的情况,这题最短路可能很大,需要注意一下inf的值

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;
typedef long long ll;

const int maxn = 6e4+10;
const ll inf = 1e15+5;

int cnt, head[maxn];
ll dis[maxn];

struct Edge {
int v, next; ll w;
bool operator < (const Edge &rhs) const {
return w > rhs.w;
}
} e[maxn*4];

void add(int u,int v,int co){
e[cnt].v = v;
e[cnt].w = co;
e[cnt].next = head[u];
head[u] = cnt++;
}

void init() {
cnt = 0;
memset(head, -1, sizeof(head));
}

void dij(int s, int len) {
priority_queue<Edge> pq;
for (int i = 1; i <= len; i++)
dis[i] = inf;
bool vis[maxn] = {0};
dis[s] = 0;
pq.push((Edge){s, 0, 0});
while (!pq.empty()) {
Edge tmp = pq.top(); pq.pop();
if (vis[tmp.v]) continue;
vis[tmp.v] = 1;
for (int i = head[tmp.v]; ~i; i = e[i].next) {
Edge u = e[i];
if (dis[u.v] > dis[tmp.v] + u.w) {
dis[u.v] = dis[tmp.v] + u.w;
pq.push((Edge){u.v, 0, dis[u.v]});
}
}
}
}


int main(){
init();
int n,m;
vector<int>v[maxn];
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++){
int k,x;
scanf("%d",&k);
while(k--){
scanf("%d",&x);
v[i].push_back(x);
}
}
for(int i = 1;i <= m;i++){
for(int j = 0;j < v[i].size();j++){
add(v[i][j],n+i*2,0);
add(n+i*2-1,v[i][j],0);
}
}
int m1,m2;
scanf("%d",&m1);
while(m1--){
int uu,vv,ww;
scanf("%d%d%d",&uu,&vv,&ww);
add(uu,vv,ww);
add(vv,uu,ww);
}
scanf("%d",&m2);
while(m2--){
int uu,vv,ww;
scanf("%d%d%d",&uu,&vv,&ww);
if(uu > vv) swap(uu,vv);
add(uu*2+n,vv*2-1+n,ww);
add(vv*2+n,uu*2-1+n,ww);
}
int s,t;
scanf("%d%d",&s,&t);
dij(s,n+m*2);
if(dis[t] == inf) dis[t] = -1;
printf("%lld\n",dis[t]);
return 0;
}