前言
- 偶然发现一道计蒜客的题,让我差分约束,最大化 \(|X_1-X_n|\)。也没多想以为是什么我不知道的。
- 于是去问 yb,他说你二分呗!这很对呀,\bx\bx 但应不止于此。
- 直到发现了这篇文章
正文
- 差分约束,我自认为再熟悉不过了,利用最短路的不等式,\(dis_u+w\ge dis_v\)
- 额,当时写差分约束时有一个问题。为什么要有一个超级源点,连向所有的点,点权为 \(0\)
- 正如大多数题解所说的,为了对于每个点跑满限制,防止图不联通。
- 嗯,但是,这真没改变我们的原图吗,其实确实改变了,但如果你只是想判断是否可行,是没有影响的。
- 自认为,最正规的做法,应该是,初始化所有点的 \(dis_u=inf\),拿出一个整个系统的钦定点,\(rt\),令 \(dis_{rt}=initval\)。
- 之后把所有点全部放入 spfa 的初始队列,这样跑的不是多源汇最短路,只是更新了所有边而已。
- 这有什么用呢?最大的用处,它没有破坏原图的性质。
- 设 \(p(u,v)\) 表示一条 \(u\) 到 \(v\) 的路径,显然有 \(dis_u+p(u,v)\ge dis_v\)
- 把 \(u\) 换成 \(rt\),\(p(rt,v)\ge dis_v-dis_{rt}\),显然 固定 \(rt,dis_{rt}\) 跑最短路,若我们想取 \(dis_v\) 的最大值,显然为 \(p(rt,v)\) 的最小值 加上 \(dis_{rt}\)。你会发现,这恰为最短路 \(dis_{v}\) 的结果。
- 同理,若是最长路,得到的就是,最小的 \(dis_v\) 的结果。
- 至此,本文结束。
#include <queue>
#include <vector>
#include <iostream>
using namespace std;
const int N = 5500;
typedef int ll;
const ll inf = 0x3f3f3f3f;
struct node { int v; ll w; };
vector<node> G[N]; ll dis[N]; int cnt[N], vis[N];
int main()
{
int n, m; cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int u, v; ll w; cin >> u >> v >> w;
G[v].push_back({ u,w });
}
queue<int> q;
for (int i = 1; i <= n; i++)q.push(i), dis[i] = inf, vis[i] = 1;
dis[1] = 0;
while (q.size())
{
int u = q.front(); q.pop(); vis[u] = 0;
for (auto [v, w] : G[u])
{
if (dis[u] + w < dis[v])
{
dis[v] = dis[u] + w; cnt[v] = cnt[u] + 1;
if (cnt[v] > n) { cout << "NO\n"; return 0; }
if (!vis[v])vis[v] = true, q.push(v);
}
}
}
for (int i = 1; i <= n; i++)cout << dis[i] << ' '; cout << '\n';
return 0;
}