P9618
Suite_No1_G · · 题解
建议评绿/蓝。
分析:
一看就是很明显的最短路问题。
观察到讯问中的
设
由于有换乘,可以想到分层图。
由于
建一个不到
用分层图跑一下最短路即可。为了节省内存,我们对
code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+10;//注意数组范围,最大为sigma(ki)+n,即4e5
int a[maxn];//输入顺序的第i个点的实际点为a[i]
int tot=0;
struct edge{//边,to表示到哪个点,tran表示是否换乘,d表示边的长度
int to;
int tran;
int d;
};
vector<edge> G[maxn];//图
int dist[maxn];
vector<int> belong[maxn];//输入顺序的第i个点的真实点
int n,m,q;
struct point{//点,用于dijkstra算法。
int u;//节点编号
int tran;//换乘了几次
int d;//距离
bool operator <(const point &cmp) const{//重载运算符
return d>cmp.d;
}
};
int dp[maxn][25];
void dij(){//dijkstra
priority_queue<point> q;
while (!q.empty()) q.pop();
for (int i=1;i<=4e5;i++){
for (int j=0;j<25;j++) dp[i][j]=1e9+7;//初始化成极大值
}
//将编号为1的点放入优先队列
for (int i=1;i<=tot;i++){
if (a[i]==1){//a[i]的定义见主函数。
q.push((point){i,0,0});
dp[i][0]=0;
}
}
while (!q.empty()){
point f=q.top(); q.pop();
int u=f.u,tran=f.tran,d=f.d;
if (d>dp[u][tran]) continue;//减少很多无用的松弛
for (int i=0;i<(int)G[u].size();i++){
int v=G[u][i].to;
if (tran+G[u][i].tran>21) continue;//特判,如果超过20则无意义
if (dp[v][tran+G[u][i].tran]>dp[u][tran]+G[u][i].d){//松弛
dp[v][tran+G[u][i].tran]=dp[u][tran]+G[u][i].d;
q.push((point){v,tran+G[u][i].tran,dp[v][tran+G[u][i].tran]});
}
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for (int i=1;i<=m;i++){
int cnt;
scanf("%d",&cnt);
if (cnt==1){//如果地铁线路长度为1,则这条地铁没有意义
int unu; scanf("%d",&unu); continue;
}
int u;
scanf("%d",&u);
a[++tot]=u;//输入的第tot个点,真实点为u
belong[u].push_back(tot);
cnt--;
while (cnt--){//前面一个点向后面的点连边
int now;
scanf("%d",&now);
a[++tot]=now;
belong[now].push_back(tot);//输入的第tot个点,真实点为now
G[tot-1].push_back((edge){tot,0,1});//路程为1,不需要换乘
}
}
for (int i=1;i<=n;i++){
int to=i+tot;//虚点
for (int j=0;j<(int)belong[i].size();j++){
int u=belong[i][j];
G[u].push_back((edge){to,0,0});
G[to].push_back((edge){u,1,0});
//如果a[x]=a[y],那么这两条单向边保证了从x到y需要换乘1次,距离为0
}
}
dij();
for (int i=0;i<=30;i++) dist[i]=1e9+7;//dist[i]为到n点换乘i次的最短路径长度,初始化为极大值
for (int i=0;i<(int)belong[n].size();i++){
int u=belong[n][i];
for (int j=0;j<=20;j++) dist[j]=min(dist[j],dp[u][j]);//更新到n点换乘j次的最短距离
}
while (q--){
int k1,k2,c;
scanf("%d%d%d",&k1,&k2,&c);
long long ans=1e12;
for (int i=0;i<=c;i++){//枚举换乘几次
// printf("%d %d\n",dist[i],i);
ans=min(ans,1ll*k1*dist[i]+1ll*k2*i);
}
printf("%lld\n",ans);
}
return 0;
}
开 O2 就能过了。