【BZOJ】4289 PA2012 Tax

传送门http://www.lydsy.com/JudgeOnline/problem.php?id=4289

出题人:http://www.cnblogs.com/clrs97/p/5046933.html

Claris大大讲得挺迷幻的……【但我没信心可以讲得比他好】
把边变成点,转换一下图。这就是第一步的构造,相信大家都能看懂。
第二步就是把原图的出边差分了,因为是无向图,所以会有一条出边和当前的入边的权值相同。那么按照Claris的方法把边处理之后,就可以通过差分构出来的边等价地转移到其他出边上。
具体构造方法:连接起终点的边和超级起点、终点连接。
每条出边和自己反向的边连边权为自己边权的边。
对于一个起点,把排完序的边按顺序建立差分的边,大的往小的边权是0,小的往大的边权是权值差。

感性认识一下:
(黑、绿是原图,红色是转换后的图,这幅图没有画出所有的连边)
(两个绿色的点在原图中表示一个点)
这里写图片描述

#include<stdio.h>
#include<queue>
#include<cstring>
#include<algorithm>
#define cint const int &
#define N 400005
using namespace std;

int ori_tot=1,tot,n,m,s[N],p[N],os[N];
bool f[N];
long long dis[N];

struct edge{int v,c,n;}e[N*5];
inline void push(cint u,cint v,cint c){e[++tot]=(edge){v,c,s[u]};s[u]=tot;}

struct ori_edge{int v,c,n;}E[N];
inline void ori_push(cint u,cint v,cint c)
{
    E[++ori_tot]=(ori_edge){v,c,os[u]};os[u]=ori_tot;
    push(ori_tot,ori_tot^1,c);
}
inline bool cmp2(cint a,cint b){return E[a].c<E[b].c;}

struct node{int a;long long b;friend bool operator < (node a,node b){return a.b>b.b;}};
priority_queue<node> Q;

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1,u,v,c;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&c);
        ori_push(u,v,c);
        ori_push(v,u,c);
    };
    for (int i=os[1];i;i=E[i].n)
    {
        push(1,i,E[i].c);
        if (E[i].v==n) push(i,ori_tot+1,E[i].c);
    }
    for (int i=2;i<=n;i++)
    {
        int ed=0;
        for (int j=os[i];j;j=E[j].n) p[ed++]=j;
        sort(p,p+ed,cmp2);
        if (E[p[0]].v==n) push(p[0],ori_tot+1,E[p[0]].c);
        for (int j=1;j<ed;j++)
        {
            push(p[j],p[j-1],0);
            push(p[j-1],p[j],E[p[j]].c-E[p[j-1]].c);
            if (E[p[j]].v==n) push(p[j],ori_tot+1,E[p[j]].c);
        }
    }
    memset(dis,0x7f,sizeof(dis));
    dis[1]=0;Q.push((node){1,0});
    for (;;)
    {
        node now=Q.top(),M=(node){0,0};Q.pop();
        while (f[now.a]) now=Q.top(),Q.pop();f[now.a]=1;
        if (f[ori_tot+1]) break;
        for (int i=s[now.a];i;i=e[i].n) if (dis[now.a]+e[i].c<dis[e[i].v])
        {
            dis[e[i].v]=dis[now.a]+e[i].c;
            Q.push((node){e[i].v,dis[e[i].v]});
        }
    }
    printf("%lld",dis[ori_tot+1]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值