题面:
给出一个 \(n\) 个点 \(m\) 条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点 1 到点 \(n\) 的最小代价。起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边权。
范围&性质:\(1\le n\le 10^5,1\le m\le 2\times 10^5\)
分析:
暴力做法:
由于图的权值在点上,所以考虑建一张新的图,将点权化作边权,相邻两边之间连一条新的边,最劣情况下空间复杂度会达到\(O(m^2)\)
优化:
结合点的权值取决于入边和出边的最大值的性质,可以利用差分思想降低总边数:
(我们规定新图中点\(i\)的出边记作\(p_i\),入边记作\(p_{i'}\))
-
将原图中每一个点的入边和出边按照权值分别从小到大排序
-
对于原图中的边\(e_i\),即新图中的\(p_i\),向\(p_{i+1}\)连一条费用为二者之差的边,再从\(p_{i+1}\)向\(p_i\)连一条费用为0的边
-
对于每个\(p_i\),向权值比它大的最小的\(p_{i'}\)连一条边,费用为\(p_{i'}\)的权值,再从权值比\(p_i\)小的最大的\(p_{i'}\)向\(p_i\)连一条边,费用为\(p_i\)的费用
这样建图的效果是:倘若出边的费用小于入边,则费用就取决于入边,因为新图中权值大的点通向权值小的点是免费的,反之则需要通过差额补足,总边数便降到\(m\)级别。
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
const int maxn = 5e5+5;
const int maxm = 5e6+5;
int n,m,cnt=0,st,ed,ecnt=0;
int head[maxn];
struct edge
{
int to,nxt;
long long val;
}e[maxm];
struct old_graph
{
int frm,to,id;
long long val;
old_graph(int frm,int to,int id,long long val): frm(frm),to(to),id(id),val(val){}
bool operator <(const old_graph &b)const
{
return val<b.val;
}
};
struct heap
{
int id;
long long d;
heap(int id,long long d):id(id),d(d){}
bool operator <(const heap &b)const
{
return d>b.d;
}
};
vector<old_graph> p[maxn];
priority_queue<heap> q;
void add(int u,int v,long long w)
{
e[++ecnt].to=v;
e[ecnt].nxt=head[u];
e[ecnt].val=w;
head[u]=ecnt;
}
long long dis[maxn];
long long dijkstra()
{
memset(dis,0x7f7f7f,sizeof(dis));
dis[st]=0;
q.push(heap(st,0));
while(!q.empty())
{
heap tmp=q.top();q.pop();
if(tmp.d != dis[tmp.id] )continue;
if(tmp.id==ed) return dis[ed];
for(int i=head[tmp.id];i;i=e[i].nxt)
{
int v=e[i].to;
if(dis[v]>dis[tmp.id]+e[i].val)
{
dis[v]=dis[tmp.id]+e[i].val;
q.push(heap(v,dis[v]));
}
}
}
}
void work()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int a,b;
long long c;
scanf("%d%d%lld",&a,&b,&c);
p[a].push_back(old_graph(a,b,++cnt,c));
p[b].push_back(old_graph(b,a,++cnt,c));
}
ed=cnt+1;
for(int i=1;i<=n;i++)
{
sort(p[i].begin(),p[i].end());
int len=p[i].size();
for(int j=1;j<len;j++)
{
add(p[i][j].id,p[i][j-1].id,0);
add(p[i][j-1].id,p[i][j].id,p[i][j].val-p[i][j-1].val);
}
}
for(int i=1;i<=n;i++)
{
int len=p[i].size();
for(int j=0;j<len;j++)
{
int v=p[i][j].to;
int size=p[v].size();
int pos=lower_bound(p[v].begin(),p[v].end(),p[i][j])-p[v].begin();
if(pos>0)
{
add(p[i][j].id,p[v][pos-1].id,p[i][j].val);
}
if(pos<size)
{
add(p[i][j].id,p[v][pos].id,p[v][pos].val);
}
}
}
int len=p[1].size();
for(int i=0;i<len;i++)
{
add(st,p[1][i].id,p[1][i].val);
}
len=p[n].size();
for(int i=0;i<len;i++)
{
add(p[n][i].id,ed,0);
}
printf("%lld\n",dijkstra());
}
}
int main()
{
zzc::work();
return 0;
}