zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    5 构建Mysql+heartbeat+DRBD+LVS集群应用系统系列之生产环境下drbd裂脑处理
    elk系列8之logstash+redis+es的架构来收集apache的日志
    elk系列7之通过grok分析apache日志
    elk系列6之tcp模块的使用
    elk系列5之syslog的模块使用
    elk系列4之kibana图形化操作
    elk系列3之通过json格式采集Nginx日志
    elk系列2之multiline模块的使用
    Docker探索系列2之镜像打包与DockerFile
    elk系列1之入门安装与基本操作
  • 原文地址:https://www.cnblogs.com/youth518/p/13674235.html
Copyright © 2011-2022 走看看