CCC2025 D
chenxi2009 · · 题解
思路
M=N-1
图是一颗树,任意两点间路径唯一。从
1\le c_i\le10
可以把每个点拆成十个走最短路,新点中每个点表示对应到达旧点的某个温度。时间复杂度
每个点出边数量 \le5
还是拆点,每个点表示对应旧点出发时的温度。令
无特殊限制
一条路径的总花费是这条路径上所有相邻的两条边温度之差。
考虑把每一条边看作一个新点,这个问题相当于是在每两条共点边之间连一条新边,新边长为两条边的温度差,求一条与
这样连边是不行的,如果有一个菊花图显然任意两条边都是共点边,需要的新边数量量级达到
我们需要从这一道题的性质上入手优化。发现新边长是新点权差,那么我们其实并不用给一个点所有的邻边都两两连新边,而是把这些边按温度排序,温度大小相邻的边之间连新边就行了,因为温度大小不相邻的边互相抵达可以通过温度在中间的点中转,而不影响消耗。
新边量级为
时间复杂度
还有一种做法,延续每个点出度
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,u,v,w;
long long dis[300000];
bool vis[300000];
vector<pair<int,int>>e[300000],ne[300000];
priority_queue<pair<long long,int>,vector<pair<long long,int>>,greater<pair<long long,int>>>q;
inline void shortest_path(){//最短路板子
while(q.size()){
int u = q.top().second;
q.pop();
if(vis[u]) continue;
vis[u] = true;
for(auto [v,w] : e[u]) if(dis[v] > dis[u] + w) q.push({dis[v] = dis[u] + w,v});
}
return;
}
int main(){
memset(dis,0x3f,sizeof(dis));
read(n,m);
for(int i = 1;i <= m;i ++) read(u,v,w),ne[u].push_back({w,i}),ne[v].push_back({w,i});//旧边只需要保存温度和边编号
for(int i = 2;i < n;i ++){
sort(ne[i].begin(),ne[i].end());//给一个点的邻边按温度排序
for(int j = 1;j < ne[i].size();j ++){//任意温度相邻的边之间连新边
e[ne[i][j].second].push_back({ne[i][j - 1].second,abs(ne[i][j].first - ne[i][j - 1].first)});
e[ne[i][j - 1].second].push_back({ne[i][j].second,abs(ne[i][j].first - ne[i][j - 1].first)});
}
}
for(auto i : ne[1]) e[0].push_back({i.second,i.first});//超级源点 0 号新点
for(auto i : ne[n]) e[i.second].push_back({m + 1,0});//超级汇点 M+1 号新点
dis[0] = 0,q.push({0,0});
shortest_path();
printf("%lld",dis[m + 1]);
return 0;
}