传送门:>Here<
给出一张有向图($n leq 10^5, m leq 5 cdot 10^5$),每个点有两个点权——水晶买入价和水晶卖出价。一个商人从1走到n,点和边可以重复走,在某个点买进,在另一个店卖出。问其能赚到的最大差价。
解题思路
如果我们枚举买进点$i$,那么接下来就是要在$i ightarrow n$中寻找卖出价的最大值。从各个点到一个固定点的路径问题,最适合采用`反向建边`。因此我们反向建边做一下。
而还要注意$i$必须是从1出发可达的(有向图存在连通性问题)。因此正向边也要做一下。
代码注意点
SPFA做的是边权,而这里是点权。要注意转换。
$Code$
/*By QiXingzhi*/ #include <cstdio> #include <queue> #include <cstring> #include <algorithm> #define r read() #define Max(a,b) (((a)>(b)) ? (a) : (b)) #define Min(a,b) (((a)<(b)) ? (a) : (b)) using namespace std; typedef long long ll; const int N = 100010; const int INF = 715827882; inline int read(){ int x = 0; int w = 1; register int c = getchar(); while(c ^ '-' && (c < '0' || c > '9')) c = getchar(); if(c == '-') w = -1, c = getchar(); while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar(); return x * w; } bool inQ[N]; int n,m,ans,x,y,z; int a[N],maxx[N],minx[N]; vector <int> G[N]; vector <int> G2[N]; queue <int> q; inline void AddEdge(int u, int v){ G[u].push_back(v); G2[v].push_back(u); } inline void SPFA1(int s){ memset(minx,0x3f,sizeof(minx)); minx[s] = a[s]; q.push(s); inQ[s] = 1; int cur,to,sz; while(!q.empty()){ cur = q.front(); q.pop(); inQ[cur] = 0; sz = G[cur].size(); for(int i = 0; i < sz; ++i){ to = G[cur][i]; if(minx[cur] < minx[to]){ minx[to] = Min(minx[cur], a[to]); if(!inQ[to]){ q.push(to); inQ[to] = 1; } } } } } inline void SPFA2(int s){ memset(inQ,0,sizeof(inQ)); while(!q.empty()) q.pop(); maxx[s] = a[s]; q.push(s); inQ[s] = 1; int cur,to,sz; while(!q.empty()){ cur = q.front(); q.pop(); inQ[cur] = 0; sz = G2[cur].size(); for(int i = 0; i < sz; ++i){ to = G2[cur][i]; if(maxx[cur] > maxx[to]){ maxx[to] = Max(maxx[cur],a[to]); if(!inQ[to]){ q.push(to); inQ[to] = 1; } } } } } int main(){ //freopen(".in","r",stdin); n = r, m = r; for(int i = 1; i <= n; ++i) a[i] = r; for(int i = 1; i <= m; ++i){ x = r, y = r, z = r; AddEdge(x,y); if(z == 2){ AddEdge(y,x); } } SPFA1(1); SPFA2(n); for(int i = 1; i <= n; ++i){ ans = Max(ans, maxx[i]-minx[i]); } printf("%d",ans); return 0; }