codeforces 567 E. President and Roads dijkstra + 求图的桥
题意:
给出一个图,n <= 10^5,给出起点s和终点t
有向边,每花费一个单位的钱可以选择一条边,把它的边权-1,但是边权必须>0,
比如边权为w,最多花费w-1,边权变为1,但是不能把边权变为0
现在要选择一条最短路从s到t
题目保证s到t至少有一条路
问这个图
1.哪些边是一定会经过的
2.哪些边是可以改变边权使得它一定会经过的,最少花费多少?
3.哪些边是无论怎么改变边权,都不满足一定会经过的
先跑一遍dijkstra,求出ds数组,ds[i]表示s到i的最短距离
再把边的方向反过来,求出dt数组,dt[i]表示t到i的最短距离
对于1
一条边一定会经过,说明每一条最短路都经过它,那么我们把所有的最短路的边拿出来,建一个新图,新图的桥就是一定会经过的边
那怎么判断边e=(u,v,w)是否属于某一条最短路呢?
如果e属于某一条最短路,一定有:
ds[u] + w + dt[v] = ds[t]
问题1解决
对于2
一条边e=(u,v,w)修改后,一定会经过它,假设修改边权花费了x:
则有:ds[u] + w - x + dt[v] = ds[t] - 1
并且:w - x > 0
即:x = ds[u] + w + dt[v] - ds[t] + 1
且:x < w
对于3
除了1的边和2的边,其余的边都属于3
这道题,本来不难,但是没有考虑2个点,debug 了好久
1.图没有自环,但是有多重边,在求图的桥的时候没有处理多重边,wa 12
2.新建的最短路图,在求桥的时候,我是调用了tagjan(1),表示从节点1开始处理,
但是对于这个新图,1可能没有和s,t在同一个联通块(题目保证s到t有路径,则s和t一定在一个联通块),wa 59
应该是调用tarjan(s)或者targan(t)
代码:
//File Name: cf567E.cpp //Created Time: 2017年08月30日 星期三 18时45分21秒 #include <bits/stdc++.h> #define LL long long #define pii pair<int,int> #define pli pair<LL,int> #define fir first #define sec second using namespace std; const int MAXN = 100000 + 5; const LL INF = 100000000000000; struct Edge{ int from,to,cost; }; Edge edge[MAXN]; LL ds[MAXN],dt[MAXN];//ds[i],dt[i]分别表示i到s,t的最短距离 vector<int> G[MAXN]; //G[u]保存edge中from=u的边的id,即在edge中的位置 bool bridge[MAXN]; //第i条边在最短路图中是bridge,则bridge[i] = true void clear_G(const int n){ for(int i(1);i<=n;++i) G[i].clear(); } priority_queue<pli> que; bool vis[MAXN]; void dijkstra(const int n,const int m,const int s,LL *ds,const bool rev){ for(int i(1);i<=n;++i) ds[i] = INF; ds[s] = 0; memset(vis,false,sizeof vis); while(!que.empty()) que.pop(); que.push(pli(0,s)); while(!que.empty()){ pli tmp = que.top(); que.pop(); int u(tmp.sec); LL dis(-tmp.fir); if(vis[u] || dis != ds[u]) continue; vis[u] = true; for(auto cur:G[u]){ int v; if(rev) v = edge[cur].from; else v = edge[cur].to; int w(edge[cur].cost); if(!vis[v] && ds[v] > ds[u] + w){ ds[v] = ds[u] + w; que.push(pli(-ds[v],v)); } } } } int dfs_clock; int pre[MAXN]; int low[MAXN]; void dfs(const int u,const int fa){ low[u] = pre[u] = ++dfs_clock; bool flag = false; for(auto cur:G[u]){ int v = edge[cur].to; if(v == u) v = edge[cur].from; if(v == fa && !flag){ flag = true; continue; } if(!pre[v]){ dfs(v,u); low[u] = min(low[v],low[u]); if(low[v] > pre[u]) bridge[cur] = true; } else if(pre[v] < low[u]) low[u] = pre[v]; } } void find_bridge(const int n,const int s){ dfs_clock = 0; memset(pre,0,sizeof pre); memset(low,0,sizeof low); memset(bridge,false,sizeof bridge); dfs(s,0); } void solve(const int n,const int m,const int s,const int t){ clear_G(n); for(int i(1);i<=m;++i) G[edge[i].from].push_back(i); dijkstra(n,m,s,ds,false); clear_G(n); for(int i(1);i<=m;++i) G[edge[i].to].push_back(i); dijkstra(n,m,t,dt,true); clear_G(n); for(int i(1);i<=m;++i){ int u(edge[i].from),v(edge[i].to),w(edge[i].cost); if(ds[u] + dt[v] + w == ds[t]){ G[u].push_back(i); G[v].push_back(i); } } find_bridge(n,s); for(int i(1);i<=m;++i){ int u(edge[i].from),v(edge[i].to),w(edge[i].cost); if(ds[u] + dt[v] + w == ds[t]){ if(bridge[i]) puts("YES"); else if(w > 1) puts("CAN 1"); else puts("NO"); } else{ LL cur = ds[u] + dt[v] + w - ds[t] + 1; if(w - cur > 0) printf("CAN %lld\n",cur); else puts("NO"); } } } int main(){ int n,m,s,t; scanf("%d %d %d %d",&n,&m,&s,&t); for(int i(1),u,v,w;i<=m;++i){ scanf("%d %d %d",&u,&v,&w); edge[i].from = u; edge[i].to = v; edge[i].cost = w; } solve(n,m,s,t); return 0; }