P11666 [JOI 2025 Final] 邮局 题解
Description
有一张
第
有
每条边一次只能运送一个包裹。节点可以存储无限多个包裹。
判断:是否能够将所有包裹都运到目的地。如果可以,还要求出到达时间最晚的包裹的到达时刻。
Solution
首先有个显然的贪心是每个时刻对于每个节点,选择距离终点最远的包裹运送。
暴力做是
考虑对于每条边计算最晚经过这条边的时间。
不妨设从早到晚经过
于是经过这条边的最小时间有个下界是
设经过
证明
根据上面的贪心策略,经过
由于只会内部阻挡,所以可以先不考虑阻挡,把它们都走到
对于树的情况用线段树合并维护上面的下界即可。
对于基环树,考虑断环为链,然后把每个基环树按照链的顺序复制两遍,并放到原先的后面。
对于一个
然后对每个基环树的
时间复杂度:
Code
#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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2024-02-22 [ABC259Ex] Yet Another Path Counting 题解