【YBTOJ】【Luogu P1073】[NOIP2009 提高组] 最优贸易
链接:
题目大意:
在一个有向图上找到一个二元组 \((u,v)\) 满足有一条 \(1\to u\to v\to n\) 的路径,并且 \(Val(v)-Val(u)\) 最大。
正文:
\(Val(v)-Val(u)\) 最大,相当于 \(Val(u)\) 尽量小且 \(Val(v)\) 尽量大。
再把路径拆成 \(1\to u, v\to n\) 分别考虑:
-
第一条路径直接求最短路(边权即点权)。
-
第二条路径反图求最长路(从 \(n\) 点出发)。
然后枚举路径 \(u\to v\) 的中介点 \(i\),接下来就是找最大的 \(\text{dist}_{v\to n}(i)-\text{dist}_{1\to u}(i)\),这里的下标就是上文的最短路和最长路。
代码:
const int N = 1e5 + 10, M = 1e6 + 10;
inline ll Read()
{
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
int n, m;
int a[N];
struct edge
{
int to, nxt, val;
}e[2][M];
int head[2][N], tot[2];
void Add(int u, int v, int w, bool I)
{
e[I][++tot[I]] = (edge) {v, head[I][u], w}, head[I][u] = tot[I];
}
int dis[2][N];
bool vis[2][N];
queue <int> q;
int com(bool I, int a, int b)
{
return !I? min(a, b) : max(a, b);
}
int Com(bool I, int a, int b)
{
return !I? a < b : a > b;
}
void SPFA(int s, bool I)
{
while(!q.empty()) q.pop();
dis[I][s] = a[s];
vis[I][s] = 1;
q.push(s);
while (!q.empty())
{
int u = q.front(); q.pop();
for (int i = head[I][u], v; i; i = e[I][i].nxt)
if(Com(I, com(I, dis[I][u], e[I][i].val), dis[I][v = e[I][i].to]))
{
dis[I][v] = com(I, dis[I][u], e[I][i].val);
if (vis[I][v]) continue;
vis[I][v] = 1;
q.push(v);
}
vis[I][u] = 0;
}
}
void Solve()
{
memset (dis[0], 127 / 3, sizeof dis[0]);
memset (dis[1], 0, sizeof dis[1]);
memset (vis, 0, sizeof vis);
SPFA(1, 0), SPFA(n, 1);
int ans = 0;
for (int i = 1; i <= n; i++)
ans = max(ans, dis[1][i] - dis[0][i]);
printf ("%d\n", ans);
return;
}
int main()
{
n = Read(), m = Read();
for (int i = 1; i <= n; i++) a[i] = Read();
for (int i = 1; i <= m; i++)
{
int u = Read(), v = Read(), opt = Read();
Add(u, v, a[v], 0), Add(v, u, a[v], 1);
if (opt - 1) Add(v, u, a[u], 0), Add(u, v, a[u], 1);
}
Solve();
return 0;
}