[CF1472G] Moving to the Capital - 最短路
Description
有 n 个结点的有向图,边长为 1,已知 di 表示 1 到 i 的最短路长,现在在第 s 个结点上,有两种操作:沿着某条边,走到一个 d 更大的点;沿着某条边,走到一个 d 小于等于当前的点(只能走一次)。求每个点出发,能走到的点中,di 最小的是多少。
Solution
一个显然的性质是,特殊操作一定会留在最后用
由于那种可以使用多次的转移边构成的一定是一个 DAG,所以可以考虑用记忆化搜索来处理最后的问题
我们先求出每个点能直接到达的 d 最小的点的 d,然后以这个为权值,跑一次搜索即可
#include <bits/stdc++.h>
using namespace std;
#define reset(x) memset(x, 0, sizeof x)
#define reset3f(x) memset(x, 0x3f, sizeof x)
namespace sp
{
const int N = 1e+6 + 5;
vector<pair<int, int>> g[N];
int n, v0 = 1, d[N], v[N];
void make(int t1, int t2, int t3)
{
g[t1].push_back(make_pair(t2, t3));
}
void reset_graph()
{
for (int i = 0; i <= n; i++)
g[i].clear();
}
void solve()
{
priority_queue<pair<int, int>> qu;
for (int i = 0; i <= n + 2; i++)
d[i] = 1e9;
for (int i = 0; i <= n + 2; i++)
v[i] = 0;
d[v0] = 0;
qu.push(make_pair(0, v0));
while (qu.size())
{
int p = qu.top().second, r = qu.top().first;
qu.pop();
if (r + d[p])
continue;
for (int i = 0; i < g[p].size(); i++)
{
int q = g[p][i].first, w = g[p][i].second;
if (d[q] > d[p] + w)
{
d[q] = d[p] + w;
qu.push(make_pair(-d[q], q));
}
}
}
}
} // namespace sp
void solve()
{
int n, m, u, v;
cin >> n;
sp::n = n;
sp::reset_graph();
cin >> m;
vector<vector<int>> g(n + 2);
for (int i = 1; i <= m; i++)
{
cin >> u >> v;
sp::make(u, v, 1);
g[u].push_back(v);
}
sp::v0 = 1;
sp::solve();
auto &d = sp::d;
vector<int> f(n + 2);
for (int i = 1; i <= n; i++)
f[i] = d[i];
for (int i = 1; i <= n; i++)
for (int j : g[i])
f[i] = min(f[i], d[j]);
vector<int> vis(n + 2);
function<int(int)> dfs = [&](int p) -> int {
if (vis[p])
return f[p];
vis[p] = 1;
for (int q : g[p])
if (d[q] > d[p])
f[p] = min(f[p], dfs(q));
return f[p];
};
for (int i = 1; i <= n; i++)
if (!vis[i])
dfs(i);
for (int i = 1; i <= n; i++)
cout << f[i] << " ";
cout << endl;
}
signed main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--)
solve();
}