hetao 国庆
Day 5
T3
第一次模拟赛切紫喵(*2700),这里有原。
(开玩笑)
Solution
Subtask 1 可能比较具有启发性。
每个人只关心:每条路径上的最大值。我们要让这个值最小。所以容易发现他们只会在最小瓶颈树上走。
由于 MST 一定是最小瓶颈树,所以我们跑 kruskal 然后把树建出来。
此时边分为两类:
- 非树边:
- 考虑 \((u, v)\) 这条边,记 \(mx\) 为树上 \((u, v)\) 路径上边权的最大值。
- 那么这条边的边权如果 \(< x\) 那么他就会代替树上的一条边。
- 如果边权 \(= x\) 则他也可能成为树边。
- 所以这条边的边权至少为 \(mx + 1\)。
- 考虑 \((u, v)\) 这条边,记 \(mx\) 为树上 \((u, v)\) 路径上边权的最大值。
- 树边:
- 考虑我们可以有哪些边来替代这条树边 \((u, v)\)。
- 考虑添加一条非树边 \((u, v)\) 进来,则你可以把树上 \((u, v)\) 路径上的边随意删一个。
- 所以这条边可以覆盖 \((u, v)\) 这条路径上的边。
- 所以我们这条边的边权至少为可以覆盖到这条边的边边权的最小值。
- 考虑我们可以有哪些边来替代这条树边 \((u, v)\)。
那对于非树边你就可以树上倍增维护路径最大值。
对于树边你就可以按边权从大到小排序,然后树剖做区间覆盖。查询单点查就行。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
int n, m, p[N], dep[N], f[19][N], z[19][N], son[N], top[N], dfn[N], cnt, sz[N], ans[N];
struct edge { int u, v, w, id; bool ok; } e[N];
vector<pair<int, int>> g[N];
void dfs(int u, int fa)
{
dep[u] = dep[f[0][u] = fa] + 1;
sz[u] = 1;
for (auto t : g[u]) {
int v = t.first, w = t.second;
if (v == fa) continue;
z[0][v] = w;
dfs(v, u); sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs1(int u, int t)
{
top[u] = t;
dfn[u] = ++cnt;
if (son[u]) dfs1(son[u], t);
for (auto k : g[u]) {
int v = k.first;
if (v != f[0][u] && v != son[u]) dfs1(v, v);
}
}
int lca(int x, int y)
{
int ans = 0;
if (dep[x] < dep[y]) swap(x, y);
for (int i = 18; i >= 0; i--) if (dep[f[i][x]] >= dep[y]) ans = max(ans, z[i][x]), x = f[i][x];
if (x == y) return ans;
for (int i = 18; i >= 0; i--) {
if (f[i][x] != f[i][y]) {
ans = max(ans, max(z[i][x], z[i][y]));
x = f[i][x], y = f[i][y];
}
}
ans = max(ans, max(z[0][x], z[0][y]));
return ans;
}
int fifa(int x) { return p[x] == x ? p[x] : p[x] = fifa(p[x]); }
bool merge(int x, int y)
{
x = fifa(x), y = fifa(y);
if (x == y) return false;
p[x] = y;
return true;
}
int tr[N << 2], tag[N << 2];
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define mid (l + r) / 2
void pushup(int rt) { tr[rt] = min(tr[ls], tr[rs]); }
void pushdown(int rt)
{
int &u = tag[rt];
if (!u) return;
tr[ls] = tr[rs] = tag[ls] = tag[rs] = u;
u = 0;
}
void update(int rt, int l, int r, int sp, int ep, int v)
{
if (sp <= l && r <= ep) return tr[rt] = tag[rt] = v, void();
pushdown(rt);
if (sp <= mid) update(ls, l, mid, sp, ep, v);
if (ep > mid) update(rs, mid + 1, r, sp, ep, v);
pushup(rt);
}
int query(int rt, int l, int r, int p)
{
if (l == r) return tr[rt];
pushdown(rt);
if (p <= mid) return query(ls, l, mid, p);
return query(rs, mid + 1, r, p);
}
void addpath(int x, int y, int w)
{
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
update(1, 1, n, dfn[top[x]], dfn[x], w);
x = f[0][top[x]];
}
if (dfn[x] > dfn[y]) swap(x, y);
update(1, 1, n, dfn[x] + 1, dfn[y], w);
}
void kruskal()
{
for (int i = 1; i <= n; i++) p[i] = i;
sort (e + 1, e + m + 1, [&](edge x, edge y) { return x.w < y.w; });
for (int i = 1; i <= m; i++) {
int u = e[i].u, v = e[i].v, w = e[i].w;
if (merge(u, v)) {
e[i].ok = 1;
g[u].emplace_back(v, w);
g[v].emplace_back(u, w);
}
}
}
int main()
{
freopen("skyline.in", "r", stdin);
freopen("skyline.out", "w", stdout);
cin.tie(0)->ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v, w; cin >> u >> v >> w;
e[i] = (edge){ u, v, w, i, 0 };
}
kruskal();
dfs(1, 0), dfs1(1, 1);
for (int j = 1; j <= 18; j++)
for (int i = 1; i <= n; i++)
f[j][i] = f[j - 1][f[j - 1][i]], z[j][i] = max(z[j - 1][i], z[j - 1][f[j - 1][i]]);
for (int i = 1; i <= 4 * n; i++) tr[i] = inf, tag[i] = 0;
for (int i = m; i >= 1; i--) {
if (e[i].ok) continue;
addpath(e[i].u, e[i].v, e[i].w);
}
for (int i = 1; i <= m; i++) {
if (e[i].ok) {
ans[e[i].id] = query(1, 1, n, dfn[(dep[e[i].u] > dep[e[i].v] ? e[i].u : e[i].v)]);
} else {
ans[e[i].id] = lca(e[i].u, e[i].v);
}
}
for (int i = 1; i <= m; i++) cout << (ans[i] == inf ? -1 : ans[i] + 1) << " ";
return 0;
}

浙公网安备 33010602011771号