题目链接

Bzoj崩了之后在洛谷偶然找到的点分好题!


在暴力的角度来说,如果我们$O(n)$枚举根节点,有没有办法在$O(n)$的时间内找到答案呢?

此时如果用树形$dp$的想法,发现是可做的,因为可以推得以下的结论:

设$x$为根节点,$d[i]$为$i$节点到$x$的距离(即深度),$g[i]$为$i$节点到最近的出入口(即叶子节点)的距离,$ans_{x}$为以$x$为根节点时的答案。

如果$d[i] \geq g[i]$,则我们可以确定,以$i$为子树,对于$x$为根时的答案贡献为$1$。

如下图:

在对于以$i$为根的子树,会对$ans_{x}$产生$1$的贡献,可以理解为一个人从$i$为根的子树的任意叶子节点出发,可以比贝茜更先到达$i$

而这种解法只需要先$dfs$两次得到$g[i]$和$d[i]$,然后一次$dfs$得到答案,复杂度为$O(n^{2})$。

但是这种做法不够理想,我们还想更快的实现。

如果我们对树的性质较为熟悉,我们知道:

$1$.对于树的某棵子树,子树有m个节点,有:$\sum du[i]=2*m-1$

$2$.对于某棵树,树有n个节点,有:$\sum du[i]=2*n-2$

$PS$:$du[i]$为$i$节点的度。

将性质$1$变形为:$1=\sum (2-du[i])$

在本题中,贡献为1的子树有一个性质,即:$d[i] \geq g[i]\& \&d[fa[i]]<g[fa[i]]$。可以理解为他的父亲贡献为子节点个数,即上图中的$i$的父亲。

所以$ans_{x}$=贡献为1的子树数量之和。这不是废话吗......

所以根据性质$1$,有:$ans_{x}=\sum_{i=1}^{n}[d[i] \geq g[i]](2-du[i])$,稍微解释一下式子的来由:

因为子树的$\sum (2-du[i])=1$,而$1$刚好是一颗子树的贡献,所以满足$d[i] \geq g[i]$的点集,可以组成$ans_{x}$那么多棵贡献为1的子树。如下图:

所以满足$g[i] \geq d[i]$的点集为上图圈出来的点,而答案为贡献为1的子树数量:$3$。


此时我们可以用点分治的想法,将:

$ans_{x}=\sum_{i=1}^{n}[d[i] \geq g[i]](2-du[i])$

求解问题变化成求解点对问题:

$ans_{x}=\sum_{i=1}^{n}[dis(x,i) \geq g[i]](2-du[i])$,$dis(x,i)$为$x$到$i$的距离。

所以设$w$为当前子树的重心,$p[i]$为$i$到重心的距离。

则$dis(x,i) \geq g[i]\rightarrow p[x]+p[i] \geq g[i]\rightarrow p[x] \geq g[i]-p[i]$

而在每次求出$p[i]$后,可以使用树状数组维护$g[i]-p[i]$,不过注意$g[i]-p[i]$会小于0,所以维护时向右移$n$的数量。

细节问题可以看代码,其余的问题欢迎提问。

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5e4;
const int inf = 2e9 + ;
struct node {
int s, e, w, next;
}edge[maxn];
int head[maxn], len;
void init() {
memset(head, -, sizeof(head));
len = ;
}
void add(int s, int e) {
edge[len].s = s;
edge[len].e = e;
edge[len].next = head[s];
head[s] = len++;
}
int root, lens, sum;
int d[maxn], du[maxn], o[maxn], vis[maxn], g[maxn], son[maxn], siz[maxn], ans[maxn];
int rt[maxn], n;
int lowbit(int x) {
return x & -x;
}
void Add(int x, int val) {
for (int i = x; i <= * n; i += lowbit(i))
rt[i] += val;
}
int query(int x) {
int ans = ;
for (int i = x; i > ; i -= lowbit(i))
ans += rt[i];
return ans;
}
void getroot(int x, int fa) {
siz[x] = , son[x] = ;
for (int i = head[x]; i != -; i = edge[i].next) {
int y = edge[i].e;
if (y == fa || vis[y])continue;
getroot(y, x);
siz[x] += siz[y];
son[x] = max(son[x], siz[y]);
}
son[x] = max(son[x], sum - siz[x]);
if (son[x] < son[root])root = x;
}
void getd(int x, int fa) {
o[++lens] = x;
for (int i = head[x]; i != -; i = edge[i].next) {
int y = edge[i].e;
if (y == fa || vis[y])continue;
d[y] = d[x] + ;
getd(y, x);
}
}
void cal(int x, int val, int add) {
lens = , d[x] = val;
getd(x, );
for (int i = ; i <= lens; i++)
Add(g[o[i]] - d[o[i]] + n, - du[o[i]]);
for (int i = ; i <= lens; i++)
ans[o[i]] += add * query(d[o[i]] + n);
for (int i = ; i <= lens; i++)
Add(g[o[i]] - d[o[i]] + n, du[o[i]] - );
}
void solve(int x) {
cal(x, , );
vis[x] = ;
for (int i = head[x]; i != -; i = edge[i].next) {
int y = edge[i].e;
if (vis[y])continue;
cal(y, , -);
sum = siz[y];
root = ;
getroot(y, );
solve(root);
}
}
void dfs1(int x, int fa, int dep) { g[x] = inf;
if (du[x] == )g[x] = ;
for (int i = head[x]; i != -; i = edge[i].next) {
int y = edge[i].e;
if (y == fa)continue;
dfs1(y, x, dep + );
g[x] = min(g[x], g[y] + );
}
}
void dfs2(int x, int fa) {
for (int i = head[x]; i != -; i = edge[i].next) {
int y = edge[i].e;
if (y == fa)continue;
g[y] = min(g[y], g[x] + );
dfs2(y, x);
}
}
int main() {
scanf("%d", &n);
init();
for (int i = , x, y; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
du[x]++, du[y]++;
}
dfs1(, , );
dfs2(, );
son[] = n, root = , sum = n, getroot(, );
solve(root);
for (int i = ; i <= n; i++) {
if (du[i] == )printf("1\n");
else printf("%d\n", ans[i]);
}
}

[洛谷P4183][USACO18JAN]Cow at Large P的更多相关文章

  1. 洛谷 P4183 - [USACO18JAN]Cow at Large P(点分治)

    洛谷题面传送门 点分治 hot tea. 首先考虑什么样的点能够对以 \(u\) 为根的答案产生 \(1\) 的贡献.我们考虑以 \(u\) 为根对整棵树进行一遍 DFS.那么对于一个点 \(v\), ...

  2. luogu P4183 [USACO18JAN]Cow at Large P

    传送门 首先考虑N^2做法,每次从一个点出发,如果到达一个点,然后到达这个点的时间\(\le\)离这个点最近的叶子距离\(di_x\),那么答案+1,否则继续找点 这个暴力很不好优化.可以这样认为,如 ...

  3. [USACO18JAN]Cow at Large G(树形DP)

    P4186 [USACO18JAN]Cow at Large G(树形DP) Luogu4186 设dp[i]表示i点需要放多少个农民.则有 \(if(near[i]-dep[i]<=dep[i ...

  4. 洛谷 P4181 [USACO18JAN]Rental Service

    P4181 [USACO18JAN]Rental Service 题意翻译 farmer john有N(1≤N≤100,000)头牛,他想赚跟多的钱,所以他准备买牛奶和出租牛.有M(1≤M≤100,0 ...

  5. 洛谷P3611 [USACO17JAN]Cow Dance Show奶牛舞蹈

    题目描述 After several months of rehearsal, the cows are just about ready to put on their annual dance p ...

  6. 洛谷P3120 [USACO15FEB]Cow Hopscotch

    题目描述 Just like humans enjoy playing the game of Hopscotch, Farmer John&apos;s cows have invented ...

  7. 【洛谷P3014】Cow Line

    题目大意:康托展开和逆康托展开模板题. 题解: 注:20!约为 2e18. 代码如下 #include <bits/stdc++.h> using namespace std; const ...

  8. 【洛谷P2966】Cow Toll Paths

    题目大意:给定 N 个节点,M 条边的无向图,边有边权,点有点权,现给出 Q 个询问,每个询问查询两个节点之间的最短路径,这里最短路径的定义是两个节点之间的最短路径与这条路径中经过的节点点权的最大值之 ...

  9. [USACO18JAN]Cow at Large P

    Description: 贝茜被农民们逼进了一个偏僻的农场.农场可视为一棵有 \(N\) 个结点的树,结点分别编号为 \(1,2,\ldots, N\) .每个叶子结点都是出入口.开始时,每个出入口都 ...

随机推荐

  1. JavaScript RegExp ——对象,语法,修饰符,方括号,元字符,量词,对象方法,对象属性

    ㈠RegExp 对象 正则表达式是描述字符模式的对象. 正则表达式用于对字符串模式匹配及检索替换,是对字符串执行模式匹配的强大工具. ㈡语法 var patt=new RegExp(pattern,m ...

  2. python 创建实例对象

    实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式. 以下使用类的名称 Employee 来实例化,并通过 __init__ 方法接收参数 ...

  3. CF 680D 堆塔

    D. Bear and Tower of Cubes time limit per test 2 seconds memory limit per test 256 megabytes input s ...

  4. R_Studio(cart算法决策树)对book3.csv数据用测试集进行测试并评估模型

    对book3.csv数据集,实现如下功能: (1)创建训练集.测试集 (2)用rpart包创建关于类别的cart算法的决策树 (3)用测试集进行测试,并评估模型 book3.csv数据集 setwd( ...

  5. [EOJ Monthly2019.11][T4]安全带

    以下是题干 D. 安全带 单点时限: 1.0 sec 内存限制: 256 MB (前面题干是一堆废话,我把它删了) 简单来说:初始给出一个 n 个点顺次连接而成的环,点有点权,边权是两个端点的点权乘积 ...

  6. Spring boot之全局异常捕捉

    在一个项目中的异常我们我们都会统一进行处理的,那么如何进行统一进行处理呢? 新建一个类GlobalDefaultExceptionHandler, 在class注解上@ControllerAdvice ...

  7. Java常考面试题整理(三)

    明天又要去面试,Good luck to me.,让我在这段时间换个新的工作吧. 41.在Java中,对象什么时候可以被垃圾回收? 参考答案: 当对象对当前使用这个对象的应用程序变得不可触及的时候,这 ...

  8. OkHttp3 使用详解

    一,简介 OkHttp 是一个高效的 HTTP 客户端,具有非常多的优势: 能够高效的执行 http,数据加载速度更快,更省流量 支持 GZIP 压缩,提升速度,节省流量 缓存响应数据,避免了重复的网 ...

  9. spring的IOC——依赖注入的两种实现类型

    一.构造器注入: 构造器注入,即通过构造函数完成依赖关系的设定.我们看一下spring的配置文件: <constructor-arg ref="userDao4Oracle" ...

  10. Ruby的异常处理

    Ruby在处理0.1+0.2是会出现精度问题: 许多语言都有类似问题,详见网址:http://0.30000000000000004.com/ Ruby的异常处理 如果异常处理范围是整个方法体,可以省 ...