bzoj4289 PA2012 Tax(优化建图+Dijkstra)

首先我们考虑把无向图变成有向图,然后化边为点,新图中两点之间距离为边权的max,然后新建源点S向1的出边化成的点连边,边权为出边边权,新建汇点T,n的入边化成的点向T连边,边权为入边边权。然后S到T的最短路就是答案。这样最坏是 O ( m 2 ) O(m^2) O(m2)的边。
考虑优化建图:因为 m a x ( a , b ) = a + m a x ( b − a , 0 ) max(a,b)=a+max(b-a,0) max(a,b)=a+max(ba,0),所以我们可以先支付a的价格,再考虑补差价。
考虑一个点i的所有出边,按权值从小到大排序,然后第j小的边向第j+1小的边连边,权值为边权差,第j+1小的边向第j小的边连边,边权为0,因为每条边都是我们用原来的无向边拆的,所以对于一个点i的入边j,一定有一条点i的出边j’存在,我们建边(j->j’)权值为边权。这样还是满足题意的,并且边数优化到了 O ( n ) O(n) O(n)

#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 200010
#define pa pair
int n,m,tot=0,h[N<<1],num=0,s=1,t;
ll dis[N<<1];
bool f[N<<1];
vector<int>a[N>>1];// out edge id
struct edge{
	int fr,to,val;
}e[N<<1];
struct Edge{
	int to,nxt,val;
}data[N*6];
inline void add(int x,int y,int val){
	data[++num].to=y;data[num].nxt=h[x];h[x]=num;data[num].val=val;
}
inline void add1(int id,int x,int y,int val){
	e[id].fr=x;e[id].to=y;e[id].val=val;a[x].push_back(id);
	if(x==1) add(s,id,val);if(y==n) add(id,t,val);
}
inline bool cmp(int x,int y){return e[x].val<e[y].val;}
inline void Dij(){
	priority_queue<pa,vector<pa>,greater<pa> >q;
	memset(dis,inf,sizeof(dis));memset(f,0,sizeof(f));
	q.push(make_pair(0,s));dis[s]=0;
	while(!q.empty()){
		int x=q.top().second;q.pop();if(f[x]) continue;f[x]=1;
		for(int i=h[x];i;i=data[i].nxt){
			int y=data[i].to;
			if(dis[x]+data[i].val<dis[y]){
				dis[y]=dis[x]+data[i].val;
				q.push(make_pair(dis[y],y));
			}
		}
	}
}
int main(){
//	freopen("a.in","r",stdin);
	scanf("%d%d",&n,&m);t=m+1<<1;
	for(int i=1;i<=m;++i){
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		add1(i<<1,x,y,z);add1(i<<1|1,y,x,z);
		add(i<<1,i<<1|1,z);add(i<<1|1,i<<1,z);
	}
	for(int i=1;i<=n;++i){
		sort(a[i].begin(),a[i].end(),cmp);
		for(int j=0;j<a[i].size();++j){
			if(j) add(a[i][j],a[i][j-1],0);
			if(j+1<a[i].size()) add(a[i][j],a[i][j+1],e[a[i][j+1]].val-e[a[i][j]].val);
		}
	}
	Dij();
	printf("%lld\n",dis[t]);
	return 0;
}

你可能感兴趣的:(bzoj,最短路)