P6880 奥运公交 / Olympic Bus 做题记录
P6880 奥运公交 / Olympic Bus
Description
给定一个 \(N\) 点 \(M\) 边的有向图,每条边从 \(U_i\) 指向 \(V_i\),经过这条边的代价为 \(C_i\)。
点编号为 \(1\) 到 \(N\)。
我们可以翻转一条边,即让他从 \(U_i\) 指向 \(V_i\) 变为从 \(V_i\) 指向 \(U_i\),这时会有 \(D_i\) 的代价产生。
你要从点 \(1\) 到点 \(N\),再从点 \(N\) 回到点 \(1\),你想知道,通过翻转一条边,或者不翻转,能得到的最小代价和为多少?
\(N \leq 200,M\leq 50000\),可能有重边。
Solution
如果不翻转任何一条边,答案就是 \(dis(1\rightarrow n) + dis(n \rightarrow 1)\)。
我们枚举翻转的边,新的答案拆为两部分:
- 从 \(1\) 走到 \(n\),\(\min(dis'(1\rightarrow n),dis'(1\rightarrow V_i)+C_i+dis'(U_i\rightarrow n))\)。
- 从 \(n\) 走到 \(1\),\(\min(dis'(n\rightarrow 1),dis'(n\rightarrow V_i)+C_i+dis'(U_i\rightarrow 1))\)。
- 翻转的代价,\(D_i\)。
我们可以预处理出 \(dis(1\rightarrow i),dis(i\rightarrow n),dis(n\rightarrow i),dis(i\rightarrow 1)\) 的值,在正反图上各跑 \(2\) 遍 Dijkstra 即可。
考虑什么样的边翻转后会满足式子中用到的 \(dis'(u,v)=dis(u,v)\)。
我们建出 \(1\rightarrow n\) 与 \(n\rightarrow 1\) 在正、反图上的最短路径树,共 \(4\) 棵。
如果一条边不在这 \(4\) 棵最短路径树上的任意一棵上,那么直接利用处理好的 \(dis\) 数组 \(O(1)\) 求答案。
否则,这样的边只有 \(O(n)\) 条,翻转边后再跑一次 Dijkstra 求答案。
注意实现中由于 \(m\) 可达 \(O(n^2)\),需要使用未经堆优化的 Dijkstra。
时间复杂度 \(O(n^3+m)\)。
int n,m;
int U[M],V[M],C[M],D[M],pre[N];
ll dis1[N],dis2[N],dis3[N],dis4[N],dis[N];
bool in1[M],in2[M],in3[M],in4[M],del[M],tmp[M],vis[N];struct Edge{int to,val,id;
};
vector<Edge> e[N][2];void Dijkstra(ll d[],bool in[],int s,int tp){for(int i=1;i<=n;i++) d[i]=LINF;for(int i=1;i<=(m<<1);i++) in[i]=0;memset(vis,0,sizeof(vis));memset(pre,0,sizeof(pre));d[s]=0;for(int _=1;_<=n;_++){int x=0; ll mn=LINF;for(int i=1;i<=n;i++){if(!vis[i]&&d[i]<mn)mn=d[i],x=i;}vis[x]=1; in[pre[x]]=1;for(Edge i:e[x][tp]){int t=i.to,w=i.val,id=i.id;if(!vis[t]&&!del[id]&&d[t]>d[x]+w){d[t]=d[x]+w;pre[t]=id;}}}
}signed main(){//效率!效率!read(n),read(m);for(int i=1;i<=m;i++){read(U[i]),read(V[i]);read(C[i]),read(D[i]);e[U[i]][0].push_back({V[i],C[i],i});e[V[i]][1].push_back({U[i],C[i],i});}for(int i=1;i<=m;i++){e[V[i]][0].push_back({U[i],C[i],i+m});e[U[i]][1].push_back({V[i],C[i],i+m});del[i+m]=1;}Dijkstra(dis1,in1,1,0);//Dis1[i] = Dis(1->i)Dijkstra(dis2,in2,n,1);//Dis2[i] = Dis(i->n)Dijkstra(dis3,in3,n,0);//Dis3[i] = Dis(n->i)Dijkstra(dis4,in4,1,1);//Dis4[i] = Dis(i->1) ll ans=dis1[n]+dis3[1];for(int i=1;i<=m;i++){if(!(in1[i]||in2[i]||in3[i]||in4[i])){ll res=D[i]+min(dis1[n],dis1[V[i]]+C[i]+dis2[U[i]])+min(dis3[1],dis3[V[i]]+C[i]+dis4[U[i]]);Ckmin(ans,res);}else{del[i]=1; del[i+m]=0;ll res=D[i];Dijkstra(dis,tmp,1,0); res+=dis[n];Dijkstra(dis,tmp,n,0); res+=dis[1];Ckmin(ans,res);del[i]=0; del[i+m]=1;}}if(ans>1e18) puts("-1");else printf("%lld\n",ans);return 0;
}