1 条题解
-
0
题解
把所有叶子同时作为源点做一次 BFS,得到
dist[u]
——农民最快从某个出口赶到结点u
所需要的时间。显然,如果 Bessie 在向某个出口前进的途中遇到一个点v
满足dist[v] ≤ depth_x(v)
(depth_x(v)
是她从起点x
走到v
的步数),那么在那个时刻农民就能截住她。因此,对每个起点x
,我们只需要统计有多少个出口的路径上会出现这样的“拦截节点”。直接枚举所有起点会达到
O(n^2)
,因此我们使用点分治。每次取一棵子树的重心c
作为切分点,计算所有经过c
的路径对答案的贡献,然后再向各个儿子递归。对于某个重心c
,我们 DFS 得到它子树中所有结点的深度dep[u]
以及dist[u]−dep[u]
的值:前者表示 Bessie 经过的时间,后者表示农民从某个出口赶到u
的“剩余余量”。只要dist[u] ≤ dep[u]
,说明在 Bessie 抵达u
前农民一定能到达该点,于是与之同路径的所有入口都必须安排农民。为了在一遍遍历中快速统计贡献,我们把结点按
dep[u]
从小到大排序,同时把(dist[u]−dep[u])
以及它对应能阻断多少条出口路径的信息排序维护。在扫描过程中,用一个指针维护已经满足dist[v]−dep[v] ≤ dep[u]
的节点数;对于这些节点关联的出口路径,每出现一次就说明需要在该出口安排一个农民,从而给所有当前扫描的结点增加 1。子树贡献算完之后,我们再对每个儿子做一次减法,以免路径被重复统计,然后继续递归点分治。最终得到的
ans[i]
就是从结点i
出发时,最少需要安排的农民数量。对于本来就是出口的结点,还需要把答案强制为 1——只要在该出口安排一名农民即可。#include<bits/stdc++.h> using namespace std; const int N = 1e5; const int M = 2e5; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; int n, rt, cnt; int head[N + 5], ver[M + 5], nxt[M + 5], deg[N + 5], tot; int vis[N + 5], dist[N + 5]; int c[N + 5]; int sz[N + 5], son[N + 5]; int ans[N + 5], dep[N + 5]; queue<int> que; void addedge(int x, int y) { ver[++tot] = y; nxt[tot] = head[x]; head[x] = tot; deg[y]++; } void pre() { for (int i = 1; i <= n; i++) { if (deg[i] == 1) { dist[i] = 0; vis[i] = 1; que.push(i); } } while (!que.empty()) { int x = que.front(); que.pop(); for (int i = head[x]; i != -1; i = nxt[i]) { int y = ver[i]; if (vis[y]) { continue; } vis[y] = 1; que.push(y); dist[y] = dist[x] + 1; } } memset(vis, 0, sizeof vis); } void get_rt(int x, int fa) { sz[x] = 1; son[x] = 0; for (int i = head[x]; i != -1; i = nxt[i]) { int y = ver[i]; if (y == fa || vis[y]) { continue; } get_rt(y, x); sz[x] += sz[y]; son[x] = max(son[x], sz[y]); } son[x] = max(son[x], cnt - sz[x]); if (son[x] < son[rt]) { rt = x; } } int nums; struct Point { int idx, dist; bool friend operator < (Point p, Point q) { return p.dist < q.dist; } }p[N + 5]; struct Node { int g, c; bool friend operator < (Node p, Node q) { return p.g < q.g; } }q[N + 5]; void get_dep(int x, int fa, int d) { dep[x] = d; for (int i = head[x]; i != -1; i = nxt[i]) { int y = ver[i]; if (y == fa || vis[y]) { continue; } get_dep(y, x, d + 1); } } void dfs(int x, int fa) { ++nums; p[nums] = (Point){x, dep[x]}; q[nums] = (Node){dist[x] - dep[x], 2 - deg[x]}; for (int i = head[x]; i != -1; i = nxt[i]) { int y = ver[i]; if (y == fa || vis[y]) { continue; } dfs(y, x); } } void calc(int x, int fa, int f) { nums = 0; dfs(x, fa); sort(p + 1, p + nums + 1); sort(q + 1, q + nums + 1); int pos = 0, sum = 0; for (int i = 1; i <= nums; i++) { while (pos + 1 <= nums && p[i].dist >= q[pos + 1].g) { sum += q[pos + 1].c; pos++; } ans[p[i].idx] += sum * f; } } void solve(int x) { vis[x] = 1; get_dep(x, 0, 0); calc(x, 0, 1); for (int i = head[x]; i != -1; i = nxt[i]) { int y = ver[i]; if (vis[y]) { continue; } calc(y, x, -1); cnt = sz[y]; rt = 0; get_rt(y, x); solve(rt); } } int main() { ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); memset(head, -1, sizeof head); cin >> n; for (int i = 1; i < n; i++) { int x, y; cin >> x >> y; addedge(x, y); addedge(y, x); } pre(); rt = 0; cnt = son[0] = n; get_rt(1, 0); solve(rt); for (int i = 1; i <= n; i++) { if (deg[i] == 1) { ans[i] = 1; } cout << ans[i] << "\n"; } return 0; }
- 1
信息
- ID
- 3261
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者