【bzoj4289: PA2012 Tax】图论--建图

来源:互联网 发布:软件开发的发展趋势 编辑:程序博客网 时间:2024/06/05 14:24



1 3 2
2 3 1
2 4 4
3 4 8

Sample Output

12



这题有个显然的暴力做法,就是把每条无向边拆成两条有向边,然后把这些有向边当做点,重新建图,对于原图中的点x,每一条入边p,要向每一条出边q连边,边的权值为p和q这两条边权值的较大值,补上源点和汇点,跑最短路就可以了。暴力的效率是O(m*m)。

我们考虑进行优化。对于暴力的做法,我们发现其中每条入边要向每条出边(这里指的入边和出边都转化为后来的点)连边,这一步直接制约了我们的效率。我们考虑如何优化这一步:

对于一个点x,我们把所有的出边按权值排序,容易知道,我们可以从权值小的边向权值大的边(这里指的连边都是把原图里的边转化为点后进行的)连边,边的权值是大权值-小权值,而对于权值大的边到权值小的边我们可以连一条权值为0的边(因为如果经过了权值较大的边,那么再经过权值较小的边时对答案的贡献仍然是那个较大的权值)。之后我们把入边加入,每条入边向对应的出边(y->x向x->y)连一条权值为原图中改边权值的边。

这样我们就把建图简化了,再加上源点和汇点就可以跑最短路了。

#include<cstdio>#include<queue>#include<vector>#include<algorithm>#include<cstring>#define Pa pair<long long,int>#define ll long long#define N 2000005#define INF 1LL<<60using namespace std;int k=1,K=1,fir[N],Fir[N],st,ed,l,r,c,n,m,b[N];ll dis[N]; struct he{int r,c,nx;}a[N],A[N];bool cmp(int u,int v){return a[u].c<a[v].c;}void add(int l,int r,int c){k++;a[k].r=r;a[k].c=c;a[k].nx=fir[l];fir[l]=k;}void Add(int l,int r,int c){K++;A[K].r=r;A[K].c=c;A[K].nx=Fir[l];Fir[l]=K;}ll dij(int st,int ed){priority_queue<Pa,vector<Pa>,greater<Pa> >Q;for(int i=st;i<=ed;i++) dis[i]=INF; Q.push(make_pair(0,st));dis[st]=0;while(!Q.empty()){int u=Q.top().second;ll Dis=Q.top().first;Q.pop();if(Dis>dis[u]) continue;for(int i=Fir[u];i!=-1;i=A[i].nx)if(dis[u]+A[i].c<dis[A[i].r])dis[A[i].r]=dis[u]+A[i].c,Q.push(make_pair(dis[A[i].r],A[i].r));}return dis[ed];}void rebuild(){st=1;ed=2*(m+1);for(int i=1;i<=n;i++){int cnt=0;for(int j=fir[i];j!=-1;j=a[j].nx)b[++cnt]=j;sort(b+1,b+1+cnt,cmp);for(int j=1;j<=cnt;j++){int x=b[j],y=b[j+1];if(a[x].r==n) Add(x,ed,a[x].c);if(i==1) Add(st,x,a[x].c);Add(x^1,x,a[x].c);if(j<cnt) Add(x,y,a[y].c-a[x].c),Add(y,x,0);}}}int main(){freopen("1.in","r",stdin);freopen("1.out","w",stdout);memset(fir,-1,sizeof(fir));memset(Fir,-1,sizeof(Fir));scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){scanf("%d%d%d",&l,&r,&c);add(l,r,c);add(r,l,c);}rebuild();printf("%lld",dij(st,ed));}



1 3 2
2 3 1
2 4 4
3 4 8

Sample Output

12
原创粉丝点击