[BZOJ4289][PA2012]Tax(最短路)

=== ===

这里放传送门

=== ===

题解

很容易想到化边为点然后如果两条边有公共点就连边,但是这样的话边数在极限情况下是 O(m2) 的显然不是很科学。。那么考虑如何减少边的数量。这里用到了一个比较常用的技巧就是利用差值转化。把每条边拆成两条有向边,这两条边之间连边权为原边权的边;然后对于每个点u,把所有从点u连出去的边排序,对于每两个相邻的边,从小的向大的连边权为差值的边,从大的向小的连边权为0的边,可以发现这样就可以满足边权取Max的限制。然后边数就压到了O(m)级别,用Dij+Heap就可以了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int n,m,tot,S,T,p[200010],a[400010],w[400010],next[400010],point[500010];
bool ext[500010];
const long long inf=1e18;
struct edge{
    int to,nxt;
    long long cst;
    void add(int x,int y,long long v){
        to=y;nxt=point[x];cst=v;point[x]=tot;
    }
}e[2000010];
struct Node{
    int pos;
    long long dis;
    bool operator < (const Node &a)const{
        return dis>a.dis;
    }
};
priority_queue<Node> q;
int comp(int x,int y){return w[x]<w[y];}
void add(int x,int y,int v){
    ++tot;a[tot]=y;next[tot]=p[x];p[x]=tot;w[tot]=v;
}
void Buildgraph(){
    int sor[400010],cnt;
    S=0;T=tot+1;
    cnt=tot;tot=0;
    for (int i=1;i<=cnt;i+=2){
        e[++tot].add(i,i+1,w[i]);
        e[++tot].add(i+1,i,w[i]);
    }
    tot=cnt;
    for (int i=1;i<=n;i++){
        cnt=0;
        for (int j=p[i];j!=0;j=next[j])
          sor[++cnt]=j;//把从点i连出去的边排序
        sort(sor+1,sor+cnt+1,comp);
        for (int j=1;j<cnt;j++){
            int x=sor[j],y=sor[j+1];
            e[++tot].add(x,y,w[y]-w[x]);//向比它大的边连边权为差值的边
            e[++tot].add(y,x,0);//向比它小的边连边权为0的边
        }
    }
    for (int i=p[1];i!=0;i=next[i])
      e[++tot].add(S,i,w[i]);
    for (int i=p[n];i!=0;i=next[i])
      e[++tot].add((i%2==0)?i-1:i+1,T,w[i]);   
/*  for (int i=S;i<=T;i++)
      for (int j=point[i];j!=0;j=e[j].nxt)
        printf("%d->%d %I64d\n",i,e[j].to,e[j].cst);*/
}
long long Dijkstra(int S,int T){
    long long ans=inf;
    Node now;
    now.pos=S;now.dis=0;
    q.push(now);
    while (!q.empty()){
        Node u=q.top();q.pop();
        if (ext[u.pos]==true) continue;
//      printf("%d\n",u);
        if (u.pos==T) return u.dis;
        for (int i=point[u.pos];i!=0;i=e[i].nxt)
          if (ext[e[i].to]==false){
              Node v;
              v.pos=e[i].to;
              v.dis=u.dis+e[i].cst;
              q.push(v);
          }
        ext[u.pos]=true;
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++){
        int x,y,v;
        scanf("%d%d%d",&x,&y,&v);
        add(x,y,v);add(y,x,v);
    }
    Buildgraph();
    printf("%lld\n",Dijkstra(S,T));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值