2018.12.30-dtoj-2659-Tax

题目描述:

给出一个N个点M条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点1到点N的最小代价。起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边权
N<=100000
M<=200000

算法标签:dijk,建边优化

思路:

考虑重新建图,容易想到把两条边合成一条边,边权是两边之间的最大值,这样边的条数是m 跑不过,考虑优化建边。对于每个顶点,我们仅把这个点的入边与和我大小相同的出边相连,因为我们把双向边拆成单向边,所以必然存在与自己的大小相等的出边。再把每条出边像比自己小的边连0,比自己大的边连权值的差值,这么建边条数是m级别的。

以下代码:

#include<bits/stdc++.h>
#define il inline
#define LL long long
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=4e5+5,M=4e6+5;
struct node{int x,v;};vector<node> v[N];
int n,m,s,t,head[N],ne[M],to[M],w[M],cnt;LL d[N];
struct data{int x;LL d;bool operator<(const data&t1)const{return d>t1.d;};};
bool cmp(node t1,node t2){return t1.v<t2.v;}priority_queue<data> q;
il int read(){int x;char ch;_(!);x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return x;}
il void insert(int x,int y,int z){ne[++cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;}
il void dijk(){
    for(int i=1;i<=t;i++)d[i]=1e18;q.push((data){s,0});
    while(!q.empty()){
        data now=q.top();q.pop();int x=now.x;if(now.d!=d[x])continue;
        for(int i=head[x];i;i=ne[i])
            if(d[to[i]]>d[x]+w[i])d[to[i]]=d[x]+w[i],q.push((data){to[i],d[to[i]]});
    }
}
int main()
{
    n=read();m=read();s=0;t=2*m+1;
    for(int i=1;i<=m;i++){
        int x=read(),y=read(),z=read();
        v[x].push_back((node){i,z});
        v[y].push_back((node){i+m,z});
        insert(i,i+m,z);insert(i+m,i,z);
    }
    for(int i=0;i<v[1].size();i++)insert(s,v[1][i].x,v[1][i].v);
    for(int i=0;i<v[n].size();i++)insert(v[n][i].x,t,0);
    for(int i=2;i<n;i++){
        sort(v[i].begin(),v[i].end(),cmp);
        for(int j=1;j<v[i].size();j++){
            insert(v[i][j-1].x,v[i][j].x,v[i][j].v-v[i][j-1].v);
            insert(v[i][j].x,v[i][j-1].x,0);
        }
    }
    dijk();printf("%lld\n",d[t]);
    return 0;
}
View Code
 
 

猜你喜欢

转载自www.cnblogs.com/Jessie-/p/10199172.html