[USACO18JAN]Lifeguards P(构造+点分治)
[USACO18JAN]Lifeguards P(构造+点分治)
题目大意
贝茜被农民们逼进了一个偏僻的农场。农场可视为一棵有 \(N\) 个结点的树,结点分别编号为 \(1,2,\ldots, N\) 。每个叶子结点都是出入口。开始时,每个出入口都可以放一个农民(也可以不放)。每个时刻,贝茜和农民都可以移动到相邻的一个结点。如果某一时刻农民与贝茜相遇了(在边上或点上均算),则贝茜将被抓住。抓捕过程中,农民们与贝茜均知道对方在哪个结点。
请问:对于结点 \(i\,(1\le i\le N)\) ,如果开始时贝茜在该结点,最少有多少农民,她才会被抓住。
数据范围
\(2 \leq N \leq 7 \cdot 10^4\)
解题思路
神题++ 贝西贝西贝西呦
设 \(deg[x],~d[x],~dep[x]\) 分别指 x 的度数,x 距离叶子节点的最短距离,以 rt 为根时,x 的深度
容易发现贝西在点 x 被截住的条件是 \(d[x] <= dep[x]\) 且 贝西没在 x 父亲处被截住,那么 x 对答案的贡献为 1。暴力 dfs 是 \(\Theta(n^2)\) 的
构造,发现 x 满足截住条件的话它的子树都会满足条件,让整个子树都产生贡献并且贡献之和为 1,即 \((\sum_{i\in subtree(x)}val[i]) = 1\),又因为 \((\sum_{i \in subtree(x)}deg[i]) = 2 \cdot m-1\),那么 \(val[x] = 2 - deg[x]\) 即可
于是可以点分治 \(ans[x] = \sum_{i = 1}^n [dist(x, i) >= d[i]] * (2-deg[i])\),利用单调性点分治 + 排序即可
时间复杂度 \(T(N) = 2 * T(\frac N2) + NlogN = Nlog^2N\)
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template<typename F>
inline void write(F x)
{
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar('\n');
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }
const int N = 200500;
int deg[N], d[N], h[N], ne[N], to[N], tot;
inline void add(int x, int y) {
ne[++tot] = h[x], deg[to[h[x] = tot] = y]++;
}
void Dfs(int x, int fa) {
if (deg[x] == 1) d[x] = 0;
for (int i = h[x]; i; i = ne[i]) {
int y = to[i]; if (y == fa) continue;
Dfs(y, x); Mn(d[x], d[y] + 1);
}
}
void Dfs(int x, int fa, int mn) {
Mn(mn, d[x]), Mn(d[x], mn);
for (int i = h[x]; i; i = ne[i])
if (to[i] != fa) Dfs(to[i], x, mn + 1);
}
int ans[N], vis[N], siz[N], sz, mn, rt, n;
void get_rt(int x, int fa) {
siz[x] = 1; int mx = 0;
for (int i = h[x]; i; i = ne[i]) {
int y = to[i]; if (vis[y] || y == fa) continue;
get_rt(y, x); siz[x] += siz[y]; Mx(mx, siz[y]);
}
Mx(mx, sz - siz[x]);
if (mx < mn) mn = mx, rt = x;
}
int sam[N], bl[N], sum, tmp, t1, t2;
pair<int, int> A[N], B[N];
void get(int x, int fa, int dep) {
bl[x] = tmp;
A[++t1] = MP(dep, x), B[++t2] = MP(d[x] - dep, x);
for (int i = h[x]; i; i = ne[i]) {
int y = to[i];
if (vis[y] || y == fa) continue;
get(y, x, dep + 1);
}
}
void solve(int x) {
vis[bl[x] = x] = 1, sam[x] = 0, t1 = t2 = 0;
A[++t1] = MP(0, x), B[++t2] = MP(d[x], x);
for (int i = h[x]; i; i = ne[i])
if (!vis[tmp = to[i]]) sam[tmp] = 0, get(tmp, x, 1);
sort(A + 1, A + t1 + 1), sort(B + 1, B + t2 + 1);
ll sum = 0, j = 0;
for (int i = 1;i <= t1; i++) {
while (j < t1 && B[j+1].fi - A[i].fi <= 0)
++j, sum += 2 - deg[B[j].se], sam[bl[B[j].se]] += 2 - deg[B[j].se];
ans[A[i].se] += sum - sam[bl[A[i].se]];
}
for (int i = h[x]; i; i = ne[i]) {
int y = to[i]; if (vis[y]) continue;
sz = siz[rt = y], mn = n, get_rt(y, 0);
solve(rt);
}
}
int main() {
read(n);
for (int i = 1, x, y; i < n ; i++)
read(x), read(y), add(x, y), add(y, x);
memset(d, 0x3f, sizeof(d));
Dfs(1, 0), Dfs(1, 0, 1e8);
rt = 1, sz = mn = n; get_rt(1, 0), solve(rt);
for (int i = 1;i <= n; i++)
write(deg[i] == 1 ? 1 : ans[i]);
return 0;
}