D82 分层图最短路 Dijkstra 算法 P13271 [NOI2025] 机器人
D82 分层图最短路 Dijkstra 算法 P13271 [NOI2025] 机器人_哔哩哔哩_bilibili
要走 $u$ 点的第 $j$ 条出边,就要先把参数 $p$ 调为 $j$,
参数 $p$ 的增减都有对应的费用,$p=1\overset{v_1}{→}p=2$,$p=2\overset{v_2}{→}p=3$;$p=2\overset{w_2}{→}p=1$,$p=3\overset{w_3}{→}p=2$
思路
如果按参数上限 k 拆点建分层图,新点数 = n*k,会爆掉
观察 $\sum d_i=m$ ,发现每个点按出边的条数拆点,增减参数就转化为在相邻拆点之间走边
没有出边的点,也要拆为一个新点,所以每个点拆为 出边条数+1 个点
图中,点 1 拆为 0,1,2,3,点 2 拆为 4,5,6,点 3 拆为 7,点 4 拆为 8,9,10...
1. 点的相邻拆点之间连双向边,分别表示增减参数的费用
2. 原点 $u,v$ 之间连边:
(1)如果 $u$ 的出边编号 $j\le v$ 的出边数量,那么直接连 $u,v$ 同级别的拆点,如 绿 1 连向 绿 5
(2)如果 $u$ 的出边编号 $j>v$ 的出边数量,从 $j$ 点连向 $v$ 的最大拆点,边权为 $z+\sum w$(减参的费用可以预处理前缀和),
如 绿 3 连向 绿 10,10 号点可以继续走向其它点;
注意:还要 绿 3 连向 绿 8 一条边权为 z 的边,表示从 黑 1 走到 黑 4,就不再移动的情况
这样,点的拆点就是分层,层与层连双向边(变参),层内连单向边(走边),建好分层图,跑一遍 Dijkstra

相关板子:
D75【模板】分层图最短路 Dijkstra 算法 P4568 [JLOI2011] 飞行路线 - 董晓 - 博客园
// 分层图最短路 Dijkstra 算法 O(NlogN) #include<bits/stdc++.h> #define int long long #define pii pair<int,int> using namespace std; const int N=6e5; vector<pii> g[N],e[N]; int T,n,m,k; int v[N],w[N],sw[N],cnt,a[N],l[N],r[N],d[N]; bool vis[N]; void dijkstra(){ memset(d,0x3f,sizeof d),d[1]=0; priority_queue<pii,vector<pii>,greater<pii> > q; q.push({0,1}); while(!q.empty()){ int u=q.top().second; q.pop(); if(vis[u])continue; vis[u]=1; for(auto [v,w]:e[u]){ if(d[v]>d[u]+w){ d[v]=d[u]+w; q.push({d[v],v}); } } } } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>T>>n>>m>>k; //n个点,m条边,k为参数上限 for(int i=1; i<k; i++) cin>>v[i]; //v增参费用 for(int i=2; i<=k; i++) cin>>w[i],sw[i]=sw[i-1]+w[i];//sw减参费用前缀和 for(int i=1; i<=n; i++){ cin>>a[i]; //i点的出边数量 l[i]=cnt,r[i]=cnt+a[i],cnt+=a[i]+1; //i点的出边编号范围[li,ri] for(int j=0,y,z; j<a[i]; j++) cin>>y>>z,g[i].emplace_back(y,z); //i向y连边 } for(int i=1; i<=n; i++){ for(int j=1; j<=a[i]; j++){ auto [y,z]=g[i][j-1]; if(j<=a[y]) e[l[i]+j].emplace_back(l[y]+j,z); //从 i 向 y 的同级拆点连边 else{ e[l[i]+j].emplace_back(l[y],z); //从i的第j拆点向y的第ly拆点连边,到ly不再移动 if(a[y]) e[l[i]+j].emplace_back(r[y],z+sw[j]-sw[a[y]]); //从i的第j拆点向y的第ry拆点连边,到ry可继续移动 } if(j>1){ e[l[i]+j-1].emplace_back(l[i]+j,v[j-1]); //i点的相邻出边点连边,增参边权v e[l[i]+j].emplace_back(l[i]+j-1,w[j]); //i点的相邻出边点连边,减参边权w } } } dijkstra(); for(int i=1; i<=n; i++){ int ans=*min_element(d+l[i],d+r[i]+1); cout<<(ans>1e18?-1:ans)<<' '; } }
// 分层图最短路 Dijkstra 算法 O(MlogN) #include<bits/stdc++.h> #define int long long #define pii pair<int,int> #define ipi pair<int,pii> using namespace std; const int N=3e5+3,inf=1e18; int n,m,k,a[N]; vector<pii>e[N]; //动态开点连边 vector<int>d[N]; //d[u][i]表示到达u点的第i条出边时的最小费用 vector<bool>vis[N]; int sv[N],sw[N]; //增减参数费用的前缀和 int get(int x,int y){ //增减参数的费用 if(x<=y)return sv[y-1]-sv[x-1]; else return sw[x]-sw[y]; } void dijkstra(){ priority_queue<ipi,vector<ipi>,greater<ipi> > q; for(int i=1;i<=a[1];i++){ d[1][i]=get(1,i); //1号点增加参数的费用 q.push({d[1][i],{1,i}}); } while(!q.empty()){ auto [u,p]=q.top().second; q.pop(); if(vis[u][p])continue; vis[u][p]=1; int v=e[u][p].first; //拿出u的第p条出边的终点v int cost0=d[u][p]+e[u][p].second; //d[u,p]+z d[v][0]=min(d[v][0],cost0); //更新不动点的费用 for(int i=1;i<=a[v];i++){ int cost=cost0+get(p,i); //d[u,p]+z+增减参数的费用 if(d[v][i]>cost){ d[v][i]=cost; //更新可动点的费用 q.push({cost,{v,i}}); } } } } signed main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int T; cin>>T>>n>>m>>k; for(int i=1;i<k;i++)cin>>sv[i],sv[i]+=sv[i-1]; for(int i=2;i<=k;i++)cin>>sw[i],sw[i]+=sw[i-1]; for(int i=1;i<=n;i++){ cin>>a[i]; e[i].assign(a[i]+1,{0,0}); d[i].assign(a[i]+1,inf); vis[i].assign(a[i]+1,0); for(int j=1,y,z;j<=a[i];j++){ cin>>y>>z; e[i][j]={y,z}; } } dijkstra(); for(int i=1;i<=n;i++){ int ans=*min_element(d[i].begin(),d[i].end()); cout<<(ans==inf?-1:ans)<<' '; } }
浙公网安备 33010602011771号