2018.12.30-dtoj-2659-Tax
题目描述:
给出一个N个点M条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点1到点N的最小代价。起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边权
N<=100000
M<=200000
算法标签:dijk,建边优化
思路:
考虑重新建图,容易想到把两条边合成一条边,边权是两边之间的最大值,这样边的条数是m2 跑不过,考虑优化建边。对于每个顶点,我们仅把这个点的入边与和我大小相同的出边相连,因为我们把双向边拆成单向边,所以必然存在与自己的大小相等的出边。再把每条出边像比自己小的边连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; }