P11666 [JOI 2025 Final] 邮局 题解

Description

有一张 NN 个节点 NN 条边的有向图,节点标号 1N1\sim N

ii 条边从节点 ii 指向节点 PiP_i(注意,可能出现 i=Pii=P_i 的情况),需要花 11 单位时间经过它。

MM 个包裹,第 jj1jM1\le j\le M)个包裹要从节点 AjA_j 运到节点 BjB_j。这些包裹全部从 00 时刻开始运送。

每条边一次只能运送一个包裹。节点可以存储无限多个包裹。

判断:是否能够将所有包裹都运到目的地。如果可以,还要求出到达时间最晚的包裹的到达时刻。

2N2×105,1M2×1052\leq N\leq 2\times 10^5,1\leq M\leq 2\times 10^5

Solution

首先有个显然的贪心是每个时刻对于每个节点,选择距离终点最远的包裹运送。

暴力做是 O(NMlogN)O(NM\log N) 的,不太能过。

考虑对于每条边计算最晚经过这条边的时间。

不妨设从早到晚经过 ipii\to p_i 这条边的所有路径的起点距离 iid1,d2,,dkd_1,d_2,\ldots,d_k,设 tit_i 表示前 ii 个路径经过 ii 的最小时间,则满足 ti=max{di,ti1+1}t_i=\max\{d_i,t_{i-1}+1\}

于是经过这条边的最小时间有个下界是 maxi=1k(di+ki)\max_{i=1}^{k}{(d_i+k-i)},容易发现 dd 递增时最小,此时可以得到一个答案的下界。

设经过 ipii\to p_i 这条边的下界为 wiw_i,可以证明每个下界都能取得到。

证明

根据上面的贪心策略,经过 ipii\to p_i 的路径在到达 ii 之前只会内部出现阻挡,终点在 ii 的子树内的点由于距离终点一定会在经过 ii 的路径后通行。

由于只会内部阻挡,所以可以先不考虑阻挡,把它们都走到 ii 之后再考虑阻挡的事情,而这得到的就是上面的下界。

对于树的情况用线段树合并维护上面的下界即可。

对于基环树,考虑断环为链,然后把每个基环树按照链的顺序复制两遍,并放到原先的后面。

对于一个 ai,bia_i,b_i 的路径,如果 aibia_i\to b_i 不经过断边,就加入 (ai,bi)(a_i,b_i)(ai+n,bi+n)(a_i+n,b_i+n)。否则加入 (ai,bi+n)(a_i,b_i+n)(ai+n,root)(a_i+n,root)

然后对每个基环树的 rootroot 跑树的做法即可。

时间复杂度:O((N+M)logN)O((N+M)\log N)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 4e5 + 5, kMaxT = kMaxN * 40;

struct Node {
int ls, rs, mx, cnt;
} t[kMaxT];

int n, m, cntc, sgt_cnt, ans;
int p[kMaxN], a[kMaxN], b[kMaxN], bel[kMaxN], pos[kMaxN];
int dfn[kMaxN], sz[kMaxN];
int rt[kMaxN], dep[kMaxN], onc[kMaxN];
std::vector<int> G[kMaxN], circ[kMaxN];
std::vector<std::pair<int, int>> vec[kMaxN];

void pushup(int x) {
t[x].cnt = t[t[x].ls].cnt + t[t[x].rs].cnt;
t[x].mx = std::max(t[t[x].ls].mx + t[t[x].rs].cnt, t[t[x].rs].mx);
if (!t[x].cnt) t[x].mx = 0;
}

int merge(int x, int y, int l, int r) {
if (!x || !y) return x + y;
if (l == r) {
t[x].cnt += t[y].cnt, t[x].mx = l + t[x].cnt - 1;
if (!t[x].cnt) t[x].mx = 0;
return x;
}
int mid = (l + r) >> 1;
t[x].ls = merge(t[x].ls, t[y].ls, l, mid);
t[x].rs = merge(t[x].rs, t[y].rs, mid + 1, r);
pushup(x);
return x;
}

void update(int &x, int l, int r, int ql, int v) {
if (!x) x = ++sgt_cnt;
if (l == r) {
t[x].cnt += v, t[x].mx = l + t[x].cnt - 1;
if (!t[x].cnt) t[x].mx = 0;
return;
}
int mid = (l + r) >> 1;
if (ql <= mid) update(t[x].ls, l, mid, ql, v);
else update(t[x].rs, mid + 1, r, ql, v);
pushup(x);
}

void getcirc() {
static int vis[kMaxN] = {0};
for (int i = 1; i <= n; ++i) {
if (vis[i]) continue;
static int stk[kMaxN];
int top = 0;
vis[i] = i, stk[++top] = i;
for (int j = p[i];; j = p[j]) {
stk[++top] = j;
if (vis[j]) break;
else vis[j] = i;
}
if (vis[stk[top]] == i) {
++cntc;
for (int j = top; j == top || stk[j] != stk[top]; --j)
circ[cntc].emplace_back(stk[j]), onc[stk[j]] = cntc;
std::reverse(circ[cntc].begin(), circ[cntc].end());
for (int i = 0; i < circ[cntc].size(); ++i) pos[circ[cntc][i]] = i + 1;
// for (auto x : circ[cntc]) std::cerr << x << ' ';
// std::cerr << '\n';
}
}
}

/*
4 5
1 5
2 5
3 5
4 5
1 5
2 5
3 5
*/

void build() {
for (int i = 1; i <= n; ++i) {
onc[i + n] = onc[i];
if (!onc[i]) {
G[p[i]].emplace_back(i), G[p[i] + n].emplace_back(i + n);
}
}
for (int i = 1; i <= cntc; ++i) {
for (int j = 0; j + 1 < circ[i].size(); ++j) {
G[circ[i][j + 1]].emplace_back(circ[i][j]);
G[circ[i][j + 1] + n].emplace_back(circ[i][j] + n);
// std::cerr << circ[i][j + 1] << ' ' << circ[i][j] << '\n';
// std::cerr << circ[i][j + 1] + n << ' ' << circ[i][j] + n << '\n';
}
G[circ[i][0] + n].emplace_back(circ[i].back());
// std::cerr << circ[i][0] + n << ' ' << circ[i].back() << '\n';
}
}

void upd(int a, int b) {
// std::cerr << "??? " << a << ' ' << b << '\n';
vec[a].emplace_back(a, 1), vec[b].emplace_back(a, -1);
}

void dfs1(int u, int rt) {
static int cnt = 0;
bel[u] = rt, dfn[u] = ++cnt, sz[u] = 1;
for (auto v : G[u]) {
if (onc[v]) continue;
dfs1(v, rt);
sz[u] += sz[v];
}
}

void dfs2(int u, int fa) {
dep[u] = dep[fa] + 1;
for (auto v : G[u]) {
assert(v != fa);
dfs2(v, u);
rt[u] = merge(rt[u], rt[v], 1, 2 * n);
}
for (auto [x, v] : vec[u]) update(rt[u], 1, 2 * n, dep[x], v);
// std::cerr << t[rt[u]].cnt << ' ' << t[rt[u]].mx << '\n';
if (t[rt[u]].cnt) {
ans = std::max(ans, t[rt[u]].mx - dep[u] + 1);
}
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> p[i];
getcirc(), build();
// for (int i = 1; i <= cntc; ++i) dfs1(circ[i].back() + n, circ[i].back() + n);
for (int i = 1; i <= n; ++i)
if (onc[i]) dfs1(i, i);
std::cin >> m;
for (int i = 1; i <= m; ++i) {
std::cin >> a[i] >> b[i];
// if (onc[a[i]] && !onc[b[i]]) return void(std::cout << "-1\n");
// if (onc[a[i]] && onc[b[i]] && onc[a[i]] != onc[b[i]]) return void(std::cout << "-1\n");
// if (!onc[a[i]] && onc[b[i]] && onc[bel[a[i]]] != onc[bel[b[i]]])
// std::cerr << a[i] << ' ' << bel[a[i]] << ' ' << onc[bel[a[i]]] << onc[b[i]] << '\n';
if (onc[b[i]] && onc[bel[a[i]]] != onc[b[i]]) return void(std::cout << "-1\n");
if (!onc[b[i]] && (dfn[a[i]] < dfn[b[i]] || dfn[a[i]] > dfn[b[i]] + sz[b[i]] - 1)) return void(std::cout << "-1\n");
int pa = pos[bel[a[i]]], pb = pos[bel[b[i]]];
if (pa <= pb) {
upd(a[i], b[i]), upd(a[i] + n, b[i] + n);
} else if (pa > pb) {
upd(a[i], b[i] + n), upd(a[i] + n, circ[onc[bel[a[i]]]].back() + n);
}
}
for (int i = 1; i <= cntc; ++i) dfs2(circ[i].back() + n, 0);
std::cout << ans << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P11666 [JOI 2025 Final] 邮局 题解
https://sobaliuziao.github.io/2025/02/22/post/c888a285.html
作者
Egg_laying_master
发布于
2025年2月22日
许可协议