P4183 [USACO18JAN] Cow at Large P 题解

Description

贝茜被农民们逼进了一个偏僻的农场。农场可视为一棵有 NN 个结点的树,结点分别编号为 1,2,,N1,2,\ldots, N 。每个叶子结点都是出入口。开始时,每个出入口都可以放一个农民(也可以不放)。每个时刻,贝茜和农民都可以移动到相邻的一个结点。如果某一时刻农民与贝茜相遇了(在边上或点上均算),则贝茜将被抓住。抓捕过程中,农民们与贝茜均知道对方在哪个结点。

请问:对于结点 i(1iN)i\,(1\le i\le N) ,如果开始时贝茜在该结点,最少有多少农民,她才会被抓住。

2N7×1042\leq N\leq 7\times 10^4

Solution

先考虑固定起点怎么做。

首先每个叶子一定是每次往父亲跳,那么设 lenilen_i 表示 iiii 子树里叶子的最短距离。

那么如果 leni>disilen_i>dis_i,则无论 ii 的子树怎么放都无法在 ii 点拦截贝茜。否则只要在离 ii 最近的点放一个奶牛就能在 ii 点拦截且贝茜不可能走到 ii 子树里的其他点。

容易发现这些放的奶牛是不重的。

所以答案就是所有 lenidisilen_i\leq dis_ilenfai>disfailen_{fa_{i}}>dis_{fa_i}ii 的个数。

这样做是 O(n2)O(n^2) 的。


考虑优化。

继续固定起点。容易发现这个题目等价于问有多少个子树,子树的根 ii 满足 lenidisilen_i\leq dis_ilenfai>disfailen_{fa_{i}}>dis_{fa_i}

degideg_i 表示 ii 的度数。

则对于一个大小为 mm 的子树 SS,且子树的根不为原树的根,那么 uSdegu=2m1\sum_{u\in S}{deg_u}=2m-1,转化一下就是:uS(2degu)=1\sum_{u\in S}{(2-deg_u)}=1

所以这个子树的价值可以换为子树里 (2degi)(2-deg_i) 的和。

又注意到如果子树的根 rr 满足 lenrdisrlen_r\leq dis_r 那么整个子树都满足这个条件。

所以整个树的答案就是:i=1n[lenidisi]×(2degi)\sum_{i=1}^{n}{[len_i\leq dis_i]\times (2-deg_i)}

然后把这个式子用点分治做即可。

时间复杂度:O(nlogn)O(n\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
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 7e4 + 5;

int n, rt;
int ans[kMaxN], f[kMaxN], deg[kMaxN], sz[kMaxN], mx[kMaxN];
bool del[kMaxN];
std::vector<std::pair<int, int>> vec;
std::vector<int> G[kMaxN];

struct BIT {
int c[kMaxN * 2];

void upd(int x, int v) {
x += n + 1;
for (; x <= 2 * n + 1; x += x & -x) c[x] += v;
}

int qry(int x) {
x += n + 1;
int ret = 0;
for (; x; x -= x & -x) ret += c[x];
return ret;
}
int qry(int l, int r) { return qry(r) - qry(l - 1); }
} bit;

void pre_dfs1(int u, int fa) {
if (G[u].size() == 1) f[u] = 0;
else f[u] = 1e9;
deg[u] = G[u].size();
for (auto v : G[u]) {
if (v == fa) continue;
pre_dfs1(v, u);
f[u] = std::min(f[u], f[v] + 1);
}
}

void pre_dfs2(int u, int fa) {
for (auto v : G[u]) {
if (v == fa) continue;
f[v] = std::min(f[v], f[u] + 1);
pre_dfs2(v, u);
}
}

void getsz(int u, int fa) {
sz[u] = 1, mx[u] = 0;
for (auto v : G[u]) {
if (v == fa || del[v]) continue;
getsz(v, u);
sz[u] += sz[v], mx[u] = std::max(mx[u], sz[v]);
}
}

void getrt(int u, int fa, int tot) {
mx[u] = std::max(mx[u], tot - sz[u]);
for (auto v : G[u]) {
if (v == fa || del[v]) continue;
getrt(v, u, tot);
}
if (mx[u] < mx[rt]) rt = u;
}

void dfs2(int u, int fa, int dis) {
ans[u] += bit.qry(-n, dis);
vec.emplace_back(f[u] - dis, 2 - deg[u]);
for (auto v : G[u]) {
if (v == fa || del[v]) continue;
dfs2(v, u, dis + 1);
}
}

void dfs1(int u, int fa) {
bit.upd(f[u], 2 - deg[u]);
vec = {{f[u], 2 - deg[u]}};
int lst = 1;
for (int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if (v == fa || del[v]) continue;
dfs2(v, u, 1);
for (int i = lst; i < vec.size(); ++i) bit.upd(vec[i].first, vec[i].second);
lst = vec.size();
}
for (auto [x, v] : vec) bit.upd(x, -v);
vec.clear(), lst = 0;
for (int i = (int)G[u].size() - 1; ~i; --i) {
int v = G[u][i];
if (v == fa || del[v]) continue;
dfs2(v, u, 1);
for (int i = lst; i < vec.size(); ++i) bit.upd(vec[i].first, vec[i].second);
lst = vec.size();
}
ans[u] += bit.qry(-n, 0);
for (auto [x, v] : vec) bit.upd(x, -v);
vec.clear();

del[u] = 1;
for (auto v : G[u]) {
if (v == fa || del[v]) continue;
rt = 0, getsz(v, u), getrt(v, u, sz[v]), dfs1(rt, 0);
}
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i < n; ++i) {
int u, v;
std::cin >> u >> v;
G[u].emplace_back(v), G[v].emplace_back(u);
}
pre_dfs1(1, 0), pre_dfs2(1, 0);
mx[0] = 1e9, getsz(1, 0), getrt(1, 0, n), dfs1(rt, 0);
for (int i = 1; i <= n; ++i) {
std::cout << (deg[i] != 1 ? ans[i] : 1) << '\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;
}

P4183 [USACO18JAN] Cow at Large P 题解
https://sobaliuziao.github.io/2024/02/10/post/43b83b8c.html
作者
Egg_laying_master
发布于
2024年2月10日
许可协议