【PA2012】Tax (最短路)

Description

The king of Byteland decided to follow a worldwide trend and introduce taxes everywhere he can. His new invention is the so-called travel tax that is paid by everyone travelling through the country.

Each Bytean road is assigned a tax rate. When passing through a town one needs to pay a tax that equals the maximum of the tax rate on the road that was used to enter the town and of the tax rate on the road that was used to exit the town. One also pays the tax in the first and the last town on the trip: there the tax amount equals the rate of the only corresponding road of the trip.

Your friend Byteasar is going for a trip from Bytetown to Bitcity. Help him plan his trip so that the amount of tax he pays is minimal.

Translation

给定一个n个点m条边的无向图,定义经过一个点的代价为进入该点的边和离开该点的边的边权的最大值,特别的,对于起点和终点,代价为进入或离开该点的边的边权,求从1走到n的最小代价。

Input

第一行:两个整数n,m
接下来m行:每行3个整数u,v,w,描述一条连接u,v,费用为w的边

Output

一个整数,即1到n的最小距离

SampleInput

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

SampleOutput

12

Solution

因为图G的权值在点上,如果想用常规的最短路算法解决本题,可以构建一张新图G’,使G’中的点对应G中 的边,边对应G中的点。

由于一个点的权值取决于入边和出边的最大值,对于G中每个入边e以及出边e’,在G’中向与其对应p和p’连一条边,费用为两者的最大值。

这样新图中的总边数为 m2 ,不能通过所有数据点。

结合点的权值取决于入边和出边的最大值的性质,可以利用差分思想降低总边数:

1.将原图中的一点的入边和出边按照权值分别从小到大排序

2.对于原图中的 ei ,即新图中的 pi ,向 pi+1 连一条费用为二者之差的边,再从 pi+1 pi 连一条费用为0的边

3.对于每个 pi ,向权值比它大的最小的 pi 连一条边,费用为 pi 的权值,再从权值比 pi 小的最大的 pi pi 连一条边,费用为 pi 的费用

这样建图的效果是:倘若出边的费用小于入边,则费用就取决于入边,因为新图中权值大的点通向权值小的点是免费的,反之则需要通过差额补足,总边数便降到 m <script id="MathJax-Element-28" type="math/tex">m</script>级别。

Code

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;

const int MAXN=4e5+10;
const int MAXM=4e6+10;

int n,m,S,T;

ll dis[MAXN];
int head[MAXN],tot;
struct edge{int to,nxt; ll c;}e[MAXM];

int pn;
struct point{
    int from,to,id; ll c;
    point(){}
    point(int u,int v,int pi,ll cost){from=u; to=v; id=pi; c=cost;}
    bool operator < (const point &rhs)const{return c<rhs.c;}
};

vector<point> P[MAXM];

struct data{
    int id; ll d;
    data(){}
    data(int v,ll dist){id=v; d=dist;}
    bool operator < (const data &rhs)const{return d>rhs.d;}
};

priority_queue<data> Q;

int Getint(){
    char ch; while(!isdigit(ch=getchar())); int v=ch-48;
    while(isdigit(ch=getchar())) v=(v<<3)+(v<<1)+ch-48;
    return v;
}

void Insert(int u,int v,ll cost){
    e[++tot].to=v; e[tot].c=cost;
    e[tot].nxt=head[u]; head[u]=tot;
}

ll Dijkstra(){
    memset(dis,0x7F,sizeof dis);
    dis[S]=0; Q.push(data(S,0));
    while(!Q.empty()){
        data now=Q.top(); Q.pop();
        if(now.d!=dis[now.id]) continue;
        if(now.id==T) return dis[T];
        for(int i=head[now.id];i;i=e[i].nxt){
            if(dis[e[i].to]>dis[now.id]+e[i].c){
                dis[e[i].to]=dis[now.id]+e[i].c;
                Q.push(data(e[i].to,dis[e[i].to]));
            }
        }
    }
}

int main(){
    int u,v,len,pos,size; ll cost;
    n=Getint(); m=Getint();
    for(int i=1;i<=m;i++){
        u=Getint(); v=Getint(); cost=Getint();
        P[u].push_back(point(u,v,++pn,cost));
        P[v].push_back(point(v,u,++pn,cost));
    }
    T=pn+1;
    for(int i=1;i<=n;i++){
        sort(P[i].begin(),P[i].end());
        len=P[i].size();
        for(int j=1;j<len;j++){
            Insert(P[i][j].id,P[i][j-1].id,0);
            Insert(P[i][j-1].id,P[i][j].id,P[i][j].c-P[i][j-1].c);
        }
    }
    for(int i=1;i<=n;i++){
        len=P[i].size();
        for(int j=0;j<len;j++){
            v=P[i][j].to; size=P[v].size();
            pos=lower_bound(P[v].begin(),P[v].end(),P[i][j])-P[v].begin();
            if(pos>0) Insert(P[i][j].id,P[v][pos-1].id,P[i][j].c);
            if(pos<size) Insert(P[i][j].id,P[v][pos].id,P[v][pos].c);
        }
    }
    len=P[1].size();
    for(int i=0;i<len;i++) Insert(S,P[1][i].id,P[1][i].c);
    len=P[n].size();
    for(int i=0;i<len;i++) Insert(P[n][i].id,T,0);
    printf("%lld\n",Dijkstra());
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值