【luogu P4183】Cow at Large P(点分治)(图论)(树状数组)

Cow at Large P

题目链接:luogu P4183

题目大意

给你一棵树,然后叶子节点可以放守卫。
然后有个人在树上,然后每个时刻那个人和守卫都可以移动,如果人和守卫相遇人就被抓了,如果人走到叶子节点他就逃走了。
然后问你对于每个树上的位置,如果一开始那个人在这里,至少要多少个守卫才能抓住他。

思路

首先不难看到一个简单的事情就是因为是树,所以如果我们把那个人一开始的位置当做根。
守卫就会往上走,时间越长就会断掉那个人越多的路。
所以我们在一个子树内就只需要选一个守卫,那我们具体看看那个子树顶端的点的位置。

然后你会发现假如守不住,那个人往这走,设那个顶端点为 x,距离最近的叶子节点是 y(因为守卫肯定是放这),一开始人在 z,那就是 dis(x,z)dis(x,y) 而且 dis(fax,z)<dis(x,y)
那这个临界点也不好搞,考虑弄的普遍一点,就先只看左边的式子满足的条件。

会发现满足的是这个顶点以及它的子树,那你能不能想个办法让这整个子树只贡献一次。
然后就是图论中的点边关系,在 n 个点的子树上,有:
ndui=2n1
(因为子树顶端的点连一个父亲边)
1=2nndui=n(2dui)

所以答案可以变成这个:
ansx=i=1n[dis(x,i)gi](2dui)
然后这个是点对问题可以用点分治,然后用个树状数组来存桶就可以搞了。

代码

#include<queue> #include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N = 7e4 + 100; int n, sz[N], root, max_root, du[N], g[N], dis[N], ans[N]; vector <int> G[N]; bool in[N]; struct SZSJ { int f[N << 1], n; void clear(int m) { n = m; for (int i = 1; i <= n; i++) f[i] = 0; } void add(int x, int y) { for (; x <= n; x += x & (-x)) f[x] += y; } int query(int x) { int re = 0; for (; x; x -= x & (-x)) re += f[x]; return re; } }T; void Init() { queue <int> q; for (int i = 1; i <= n; i++) if (du[i] == 1) q.push(i), in[i] = 1; while (!q.empty()) { int now = q.front(); q.pop(); for (int i = 0; i < G[now].size(); i++) { int x = G[now][i]; if (in[x]) continue; g[x] = g[now] + 1; in[x] = 1; q.push(x); } } memset(in, 0, sizeof(in)); dis[0] = -1; } void dfs0(int now, int father) { sz[now] = 1; dis[now] = dis[father] + 1; for (int i = 0; i < G[now].size(); i++) { int x = G[now][i]; if (x == father || in[x]) continue; dfs0(x, now); sz[now] += sz[x]; } } void get_root(int now, int father, int sum) { int maxn = sum - sz[now]; for (int i = 0; i < G[now].size(); i++) { int x = G[now][i]; if (x == father || in[x]) continue; get_root(x, now, sum); maxn = max(maxn, sz[x]); } if (maxn < max_root) max_root = maxn, root = now; } int dadi; void dfs1(int now, int father) { ans[now] += T.query(dis[now] + dadi); for (int i = 0; i < G[now].size(); i++) { int x = G[now][i]; if (x == father || in[x]) continue; dfs1(x, now); } } void dfs2(int now, int father) { T.add(g[now] - dis[now] + dadi, 2 - du[now]);//加sz[now]防止负号 for (int i = 0; i < G[now].size(); i++) { int x = G[now][i]; if (x == father || in[x]) continue; dfs2(x, now); } } void clac(int now) { dadi = sz[now]; T.clear(sz[now] * 2); for (int i = 0; i < G[now].size(); i++) { int x = G[now][i]; if (in[x]) continue; dfs1(x, now); dfs2(x, now); } T.clear(sz[now] * 2); T.add(g[now] + dadi, 2 - du[now]); for (int i = G[now].size() - 1; i >= 0; i--) { int x = G[now][i]; if (in[x]) continue; dfs1(x, now); dfs2(x, now); } ans[now] += T.query(dadi); } void work(int now) { in[now] = 1; dfs0(now, 0); clac(now); for (int i = 0; i < G[now].size(); i++) { int x = G[now][i]; if (in[x]) continue; max_root = 2e9; get_root(x, now, sz[x]); work(root); } } int main() { scanf("%d", &n); for (int i = 1; i < n; i++) { int x, y; scanf("%d %d", &x, &y); G[x].push_back(y); G[y].push_back(x); du[x]++; du[y]++; } Init(); dfs0(1, 0); max_root = 2e9; get_root(1, 0, n); work(root); for (int i = 1; i <= n; i++) if (du[i] != 1) printf("%d\n", ans[i]); else printf("1\n"); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P4183.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示