[BZOJ4289][PA2012]Tax

bzoj

sol

把一条边拆成两个点。
考虑这样一个东西:\(\max(a,b)=a+\max(b-a,0)\)
那么我们在每次进入一个点之前把他的入边的权值即\(a\)加上,在这个点的内部把\(\max(b-a,0)\)加上。
如果\(b>a\)那么就需要加上额外代价,如果\(b\le a\)额外代价就是零。
把连接这个点的所有边按权值排序,从下往上相邻连费用为权值差的边,从上往下相邻连0的边。
新建源点,向所有连1号点的边连费用为权值的边。
新建汇点,所有连n号点的边向它连费用为0的边。

code

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<queue>
#define ll long long
#define pli pair<ll,int>
#define mk make_pair
using namespace std;
int gi()
{
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 4e5+5;
int n,m,to[N<<3],nxt[N<<3],head[N],cnt,vis[N];ll ww[N<<3],dis[N];
vector<pli>S[N];
priority_queue<pli,vector<pli>,greater<pli> >Q;
void link(int u,int v,ll w)
{
	to[++cnt]=v;nxt[cnt]=head[u];ww[cnt]=w;
	head[u]=cnt;
}
void dijkstra()
{
	memset(dis,63,sizeof(dis));
	dis[2*m+1]=0;Q.push(mk(0,2*m+1));
	while (!Q.empty())
	{
		int u=Q.top().second;Q.pop();
		if (vis[u]) continue;vis[u]=1;
		for (int e=head[u];e;e=nxt[e])
			if (dis[to[e]]>dis[u]+ww[e])
				dis[to[e]]=dis[u]+ww[e],Q.push(mk(dis[to[e]],to[e]));
	}
}
int main()
{
	n=gi();m=gi();
	for (int i=1;i<=m;++i)
	{
		int u=gi(),v=gi();if (u>v) swap(u,v);
		ll w=gi();
		S[u].push_back(mk(w,i));
		S[v].push_back(mk(w,i+m));
		link(i,i+m,w);link(i+m,i,w);
		if (u==1) link(2*m+1,i,w);
		if (v==n) link(i+m,2*m+2,0);
	}
	for (int i=1;i<=n;++i)
	{
		sort(S[i].begin(),S[i].end());
		for (int j=1,sz=S[i].size();j<sz;++j)
		{
			link(S[i][j-1].second,S[i][j].second,S[i][j].first-S[i][j-1].first);
			link(S[i][j].second,S[i][j-1].second,0);
		}
	}
	dijkstra();
	printf("%lld\n",dis[2*m+2]);return 0;
}
posted @ 2018-03-28 14:45  租酥雨  阅读(172)  评论(0编辑  收藏  举报