[JOI 2025 Final] 邮局 / Post Office
先假设这个过程在树上进行。
考虑暴力,模拟这个过程,对一条边选择哪个点移动进行决策。发现一定是移动剩余距离最大的点,因为如果移动距离较小的点,调整一定不劣。
但是模拟这个过程无法优化,没有前途。调整思路,不关注时间,而关注每条边,答案显然等于所有边最后经过时间的最大值,如果能计算出每个点 \(i\) 到 \(x\) 的时间,那么 \((x,p_x)\) 最后经过时间可以通过上述贪心求出。因为每次移动剩余距离最大的点,我们只需要关心所有经过 \((x,p_x)\) 的路径的出发点 \(a_i\)。
考察特殊情况,如果只有 \(1\) 条路径,那么显然到达 \(x\) 的时间为 \(d_{a_i}-d_{x}\),其中 \(d_i\) 表示 \(i\) 点的深度。
拓展到一般情况,发现会出现某个时刻,两个点在同一位置,造成堵塞的情况。这种情况不好考虑,不妨猜测不用考虑这种情况,直接另 \(a_i\) 到 \(x\) 的时间为 \(d_{a_i}-d_{x}\) 而不影响答案。证明考虑如果 \(u,v\) 两点 \(t\) 时刻在 \(z\) 点堵塞了,那么到达 \(p_z\) 的时刻应为 \(t+1,t+2\),但是观察到我们可以将堵塞时间延后,将 \((z,p_z)\) 边扩容,到达 \(p_z\) 时刻变为 \(t+1,t+1\),但是他们在 \(p_z\) 仍会堵塞,那么得到一个结论:将 \((x,p_x)\) 子树内的边扩容为 \(+\infty\),不影响 \((x,p_x)\) 答案的计算。
如果得到到达 \(x\) 的时间集合 \(T\),考虑如何计算 \((x,p_x)\) 的贡献。显然最后经过时刻具有单调性,考虑二分时间 \(t\),判断能否再 \(\le t\) 时间走完这条边。
考虑 hall 定理,相当于每个时刻 \(t_i\) 可以连边 \((t_i,t]\),判断是否存在完美匹配。为了方便,平移一格匹配,也就是 \(t_i\) 可以连边 \([t_i,t]\),将求出的时刻 \(t\) 加一即可。只需要考虑所有后缀 \((i,t]\),是否都满足 \(|N(S)|\ge |S|\),即 \(t-i\ge s_{t}-s_{i}\),其中 \(s_i\) 表示时刻小于等于 \(i\) 的点的个数,条件等价于 \(t-s_{t}\ge i-s_{i}\)。
这个信息可以通过线段树合并维护区间 \(\max\{i-s_i\}\),线段树上二分查询得到。
拓展到基环树上,考虑断环为链,就可以考虑到所有经过环上的路径了,注意到复制的环上的点会正确地考虑到所有信息。
注意到二分的位置需要包含所有子树中目前存在时刻,我先维护区间时刻出现次数和,求出但前最大时刻 \(pos\),在 \([pos,+\infty]\) 上二分。
线段树合并需要进行区间修改,为了减少空间,我使用了标记永久化。
代码写得很复杂,建议参考其他的题解。
#include <bits/stdc++.h>
#define file_in(x) (freopen(#x".in", "r", stdin))
#define file_out(x) (freopen(#x".out", "w", stdout))
#define vi vector
#define pr pair <int, int>
#define mk make_pair
#define pb push_back
using namespace std;
bool START;
const int N = 4e5 + 5, M = 100, inf = 1e9;
int n, m, p[N], a[N], b[N];
int stk[N], tot, vis[N];
int d[N], idx, dy[N];
int dfn[N], sz[N], tp[N], dep[N], mxd[N], cnt;
int v[N];
int fr[N], o;
int lim;
int rt[N], mx[N * M], ex[N * M], tag[N * M], ls[N * M], rs[N * M], num;
int ans;
vi <int> G[N], q, id[N];
vi <pr> vc[N];
bool END;
void add_e(int x, int y) {G[x].pb(y);}
void dfs(int x) {
if (v[x]) {
bool fl = 0;
for (int i = 1; i <= tot; ++i) {
if (stk[i] == x) fl = 1;
if (fl) d[++idx] = stk[i];
}
return;
}
stk[++tot] = x, v[x] = 1, dfs(p[x]);
}
void dfs2(int x, int TP) {
dfn[x] = ++cnt, sz[x] = 1, tp[x] = TP, fr[x] = o, q.pb(x);
for (auto y : G[x]) if (!vis[y]) dep[y] = dep[x] + 1, dfs2(y, TP), sz[x] += sz[y];
}
void psu(int l, int r, int x) {
int mid = (l + r) >> 1;
mx[x] = max(ls[x] ? mx[ls[x]] : mid, rs[x] ? mx[rs[x]] : r) + tag[x];
ex[x] = ex[ls[x]] + ex[rs[x]];
}
void work(int k, int p) {tag[p] -= k, mx[p] -= k;}
int nnd(int l, int r) {
return num++, mx[num] = r, ls[num] = rs[num] = ex[num] = tag[num] = 0, num;
}
int mrg(int l, int r, int x, int y) {
if (!x || !y) return x | y;
tag[x] += tag[y];
if (l == r)
return mx[x] = r + tag[x], ex[x] += ex[y], x;
int mid = (l + r) >> 1;
ls[x] = mrg(l, mid, ls[x], ls[y]);
rs[x] = mrg(mid + 1, r, rs[x], rs[y]);
return psu(l, r, x), x;
}
void add(int l, int r, int L, int R, int k, int & p) {
if (!p) p = nnd(l, r);
if (l >= L && r <= R) return work(k, p);
int mid = (l + r) >> 1;
if (L <= mid) add(l, mid, L, R, k, ls[p]);
if (R > mid) add(mid + 1, r, L, R, k, rs[p]);
psu(l, r, p);
}
void addex(int l, int r, int ps, int k, int & p) {
if (!p) p = nnd(l, r);
if (l == r) return ex[p] += k, void();
int mid = (l + r) >> 1;
ps <= mid ? addex(l, mid, ps, k, ls[p]) : addex(mid + 1, r, ps, k, rs[p]);
psu(l, r, p);
}
int getp(int l, int r, int p) {
if (!ex[p]) return -1;
if (l == r) return l;
int mid = (l + r) >> 1;
if (ex[rs[p]] > 0) return getp(mid + 1, r, rs[p]);
return getp(l, mid, ls[p]);
}
int ask(int l, int r, int L, int R, int & premx, int k, int p) {
int mid = (l + r) >> 1;
if (!ls[p]) ls[p] = nnd(l, mid);
if (!rs[p]) rs[p] = nnd(mid + 1, r);
if (l >= L && r <= R) {
if (l == r) {
if (mx[p] + k >= premx) return l;
return -1;
}
k += tag[p];
if (mx[ls[p]] + k >= premx)
return ask(l, mid, L, R, premx, k, ls[p]);
else if (mx[rs[p]] + k >= (premx = max(premx, mx[ls[p]] + k)))
return ask(mid + 1, r, L, R, premx, k, rs[p]);
else premx = max(premx, mx[rs[p]] + k);
return -1;
}
k += tag[p];
if (L <= mid) {
int t = ask(l, mid, L, R, premx, k, ls[p]);
if (t != -1) return t;
}
premx = max(premx, mx[ls[p]] + k);
return ask(mid + 1, r, L, R, premx, k, rs[p]);
}
void dfs3(int x) {
mxd[x] = dep[x];
for (auto y : G[x]) {
if (x == d[1] + n && y == d[idx] + n) continue;
if (x == d[1] && y == d[idx]) continue;
dfs3(y);
rt[x] = mrg(0, lim, rt[x], rt[y]), mxd[x] = max(mxd[x], mxd[y]);
}
for (auto [t, v] : vc[x]) {
add(0, lim, t, lim, v, rt[x]);
addex(0, lim, t, v, rt[x]);
}
int tmp = -inf, pos = getp(0, lim, rt[x]);
if (pos != -1) {
int tim = ask(0, lim, pos, lim, tmp, 0, rt[x]);
ans = max(ans, tim - dep[x] + 1);
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n; for (int i = 1; i <= n; ++i) cin >> p[i];
cin >> m; for (int i = 1; i <= m; ++i) cin >> a[i] >> b[i], id[a[i]].pb(i);
for (int i = 1; i <= n; ++i) if (!vis[i]) add_e(p[i], i), add_e(p[i] + n, i + n);
lim = max(n, m) * 2 + 5;
int fl = 1;
for (int s = 1; s <= n; ++s) {
if (v[s]) continue;
idx = tot = cnt = num = 0, ++o, q.clear();
dfs(s);
for (int i = 1; i <= idx; ++i) {
d[i + idx] = d[i] + n;
dy[d[i]] = i, vis[d[i]] = 1;
dy[d[i] + n] = i + idx, vis[d[i] + n] = 1;
}
add_e(d[idx + 1], d[idx]);
for (int i = 1; i <= 2 * idx; ++i)
dep[d[i]] = 2 * idx - i, dfs2(d[i], d[i]);
for (int w : q) {
for (auto i : id[w]) {
if (fr[b[i]] != o) {fl = 0; break;}
vc[a[i]].pb(mk(dep[a[i]], 1)), vc[a[i] + n].pb(mk(dep[a[i] + n], 1));
if (vis[b[i]]) {
int l = dy[tp[a[i]]], r = dy[b[i]];
if (l <= r)
vc[b[i]].pb(mk(dep[a[i]], -1)), vc[b[i] + n].pb(mk(dep[a[i] + n], -1));
else vc[b[i] + n].pb(mk(dep[a[i]], -1));
}
else {
vc[b[i]].pb(mk(dep[a[i]], -1)), vc[b[i] + n].pb(mk(dep[a[i] + n], -1));
if (!(tp[a[i]] == tp[b[i]] && dfn[a[i]] >= dfn[b[i]] && dfn[a[i]] < dfn[b[i]] + sz[b[i]])) {
fl = 0; break;
}
}
}
}
if (!fl) return cout << -1 << endl, 0;
dfs3(d[2 * idx]);
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号