P6822 [PA2012]Tax 最短路变形

题面:

给出一个 \(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'}\))

  1. 将原图中每一个点的入边和出边按照权值分别从小到大排序

  2. 对于原图中的边\(e_i\),即新图中的\(p_i\),向\(p_{i+1}\)连一条费用为二者之差的边,再从\(p_{i+1}\)\(p_i\)连一条费用为0的边

  3. 对于每个\(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;
}
posted @ 2020-09-15 17:27  youth518  阅读(134)  评论(0编辑  收藏  举报