CSP2019 Day2T3 树的重心
显然如果我们直接枚举断哪条边,剩下的两颗树的重心编号非常不好求,一个常见的想法是我们反过来,考虑每个节点有多少种情况作为树的重心,计算每个点对答案的贡献。
下面我们就需要大力分类讨论了。假设我们现在考虑计算贡献的节点为 \(x\),令以其儿子为根的子树树的最大的子树大小为 \(mx_x\),次大值为 \(se_x\),以 \(x\) 为根的子树大小为 \(S_x\),假设 \(x\) 的一个祖先 \(f\) 断开了其与父亲的边。那么如果 \(x\) 要作为以 \(f\) 为根的树的重心,那么需要满足下面一个条件:
S_f - S_x \le \lfloor \frac{S_f}{2} \rfloor\\
mx_x \le \lfloor \frac{S_f}{2} \rfloor
\end{cases}\\\]
因为我们需要统计的是满足条件的 \(y\) 的形式,因此我们尽量将原式化简成关于 \(y\) 的一个范围。实际上,上面那个柿子能改写成 \(2 \times S_f - 2 \times S_x \le S_f\),移项可得 \(S_f \le 2 \times S_x\),类似地下面那个柿子有 \(S_f \ge 2 \times mx_x\),于是我们只需要统计一个点 \(x\) 到根的路径上有多少个点满足 \(2 \times mx_x \le S_f \le 2 \times S_x\) 即可。这个我们可以使用树状数组来做,进入每个点是将 \(S_x\) 加入树状数组,出去时删除,那么递归到每个点时的树状数组就是加入 \(x\) 到根路径上点的树状数组了,如果硬核数据结构也可以用主席树实现。
再考虑第二种情况,断掉 \(x\) 子树内的一个点 \(y\) 与其父亲的连边的情况。又要分两种情况,当 \(y\) 在 \(x\) 的重儿子内时,需要满足:
se_x \le \lfloor \frac{S_1 - S_y}{2} \rfloor\\
mx_x - S_y \le \lfloor \frac{S_1 - S_y}{2} \rfloor\\
S_1 - S_x \le \lfloor \frac{S_1 - S_y}{2} \rfloor
\end{cases}\]
同理上面的化简可得:\(2 \times mx_x - S_1 \le S_y \le \min\{S_1 - 2 \times se_x, 2 \times S_x - S_1\}\),实际上这里我们需要的是统计子树内权值在某个范围的数量,可以使用线段树合并解决。
再考虑如果 \(y\) 在 \(x\) 的非重儿子内部,那么我们需要满足:
mx_x \le \lfloor \frac{S_1 - S_y}{2} \rfloor\\
S_1 - S_x \le \lfloor \frac{S_1 - S_y}{2} \rfloor
\end{cases}\]
可以得出 \(S_y \le \min\{S_1 - 2 \times mx_x, 2 \times S_x - S_1\}\),同样是查询子树信息,用线段树合并解决。
我们再考虑最后一种情况,当不为 \(x\) 的祖先且不在 \(x\) 子树内的一个点 \(y\) 断掉了其与父亲的连边时,需要满足:
S_1 - S_y - S_x \le \lfloor \frac{S_1 - S_y}{2} \rfloor\\
mx_x \le \lfloor \frac{S_1 - S_y}{2} \rfloor
\end{cases}\]
化简可得:\(S_1 - 2 \times S_x \le S_y \le S_1 - 2 \times mx_x\)。
这个时候我被难住了,我们怎么知道不包含 \(x\) 的祖先和其子树内的点满足条件的所有 \(y\) 呢?其实很简单,上面我们已经知道了怎么统计在 \(x\) 的子树和其子树内满足条件的方法了,我们直接考虑容斥即可。考虑使用全局满足条件的点数减去在 \(x\) 的祖先和其子树内满足条件的点数即可,这个全局满足条件的点可以使用 \(ton\) 求出。
代码细节比较多,想清楚再码。
#include<bits/stdc++.h>
using namespace std;
#define N 300000 + 5
#define M 6000000 + 5
#define ls t[p].l
#define rs t[p].r
#define mid (l + r) / 2
#define lowbit(x) (x & (-x))
#define rep(i, l, r) for(int i = l; i <= r; ++i)
#define Next(i, u) for(int i = h[u]; i; i = e[i].next)
struct edge{
int v, next;
}e[N << 1];
struct tree{
int l, r, sum;
}t[M];
long long ans;
int T, n, u, v, tot, cnt, h[N], c[N], s[N], rt[N], mx[N], se[N], ton[N];
int read(){
char c; int x = 0, f = 1;
c = getchar();
while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
void Add(int u, int v){
e[++cnt].v = v, e[cnt].next = h[u], h[u] = cnt;
e[++cnt].v = u, e[cnt].next = h[v], h[v] = cnt;
}
void add(int p, int k){
for(int i = p; i <= n; i += lowbit(i)) c[i] += k;
}
int ask(int p){
int ans = 0; p = min(p, n);
for(int i = p; i >= 1; i -= lowbit(i)) ans += c[i];
return ans;
}
void update(int &p, int l, int r, int x, int y, int k){
if(!p) p = ++tot; t[p].sum += k;
if(l == r) return;
if(mid >= x) update(ls, l, mid, x, y, k);
if(mid < y) update(rs, mid + 1, r, x, y, k);
}
int query(int p, int l, int r, int x, int y){
if(!p || l > r) return 0;
if(l >= x && r <= y) return t[p].sum;
int ans = 0;
if(mid >= x) ans += query(ls, l, mid, x, y);
if(mid < y) ans += query(rs, mid + 1, r, x, y);
return ans;
}
void Merge(int &p, int k, int l, int r){
if(!p || !k){ p = p + k; return;}
if(l == r){ t[p].sum += t[k].sum; return;}
Merge(ls, t[k].l, l, mid), Merge(rs, t[k].r, mid + 1, r);
t[p].sum = t[ls].sum + t[rs].sum;
}
void dfs1(int u, int fa){
s[u] = 1;
Next(i, u){
int v = e[i].v; if(v == fa) continue;
dfs1(v, u), s[u] += s[v];
if(s[v] >= mx[u]) se[u] = mx[u], mx[u] = s[v];
else if(s[v] > se[u]) se[u] = s[v];
}
}
void dfs2(int u, int fa){
int tmp = ask(2 * s[u]) - ask(2 * mx[u] - 1) - (ask(s[1] - 2 * mx[u]) - ask(s[1] - 2 * s[u] - 1));
if(mx[u] <= s[u] / 2 && u != 1) ++tmp;
if(s[1] >= 2 * mx[u] && s[1] <= 2 * s[u] && u != 1) --tmp;
add(s[u], 1);
Next(i, u){
int v = e[i].v; if(v == fa) continue;
dfs2(v, u), Merge(rt[u], rt[v], 1, n);
if(s[v] == mx[u]) tmp += query(rt[v], 1, n, max(1, 2 * mx[u] - s[1]), min(s[1] - 2 * se[u], 2 * s[u] - s[1]));
else tmp += query(rt[v], 1, n, 1, min(s[1] - 2 * mx[u], 2 * s[u] - s[1]));
}
add(s[u], -1), update(rt[u], 1, n, s[u], s[u], 1);
tmp -= query(rt[u], 1, n, max(1, s[1] - 2 * s[u]), s[1] - 2 * mx[u]);
if(s[1] - 2 * mx[u] >= 1) tmp += ton[s[1] - 2 * mx[u]];
if(s[1] - 2 * s[u] >= 1) tmp -= ton[s[1] - 2 * s[u] - 1];
ans += 1ll * tmp * u;
}
int main(){
T = read();
while(T--){
memset(h, 0, sizeof(h)), memset(rt, 0, sizeof(rt));
memset(s, 0, sizeof(s)), memset(mx, 0, sizeof(mx));
memset(se, 0, sizeof(se)), memset(ton, 0, sizeof(ton));
rep(i, 1, tot) t[i].l = t[i].r = t[i].sum = 0;
tot = cnt = ans = 0;
n = read();
rep(i, 1, n - 1) u = read(), v = read(), Add(u, v);
dfs1(1, 0);
rep(i, 1, n) ++ton[s[i]];
rep(i, 1, n) ton[i] = ton[i - 1] + ton[i];
dfs2(1, 0);
printf("%lld\n", ans);
}
return 0;
}
CSP2019 Day2T3 树的重心的更多相关文章
- CSP2019 树的重心 题解
本题当然可以通过大力讨论每棵子树的size的大小关系,然后用各种数据结构暴力维护.但是我更倾向于用一种更为性质的做法. 首先讲一下我在考场上想到的做法(没写).就是考虑换根,在换根的过程中计算每一条边 ...
- POJ3107Godfather[树形DP 树的重心]
Godfather Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 6121 Accepted: 2164 Descrip ...
- poj1655 树的重心 树形dp
树的重心定义为:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡. 处理处每个节点的孩子有几个,和树的大小就好了. #include< ...
- poj3107 求树的重心(&& poj1655 同样求树的重心)
题目链接:http://poj.org/problem?id=3107 求树的重心,所谓树的重心就是:在无根树转换为有根树的过程中,去掉根节点之后,剩下的树的最大结点最小,该点即为重心. 剩下的数的 ...
- 树形DP求树的重心 --SGU 134
令一个点的属性值为:去除这个点以及与这个点相连的所有边后得到的连通分量的节点数的最大值. 则树的重心定义为:一个点,这个点的属性值在所有点中是最小的. SGU 134 即要找出所有的重心,并且找出重心 ...
- 求树的重心(POJ1655)
题意:给出一颗n(n<=2000)个结点的树,删除其中的一个结点,会形成一棵树,或者多棵树,定义删除任意一个结点的平衡度为最大的那棵树的结点个数,问删除哪个结点后,可以让平衡度最小,即求树的重心 ...
- codeforces 685B Kay and Snowflake 树的重心
分析:就是找到以每个节点为根节点的树的重心 树的重心可以看这三篇文章: 1:http://wenku.baidu.com/link?url=yc-3QD55hbCaRYEGsF2fPpXYg-iO63 ...
- POJ 1655 Balancing Act (求树的重心)
求树的重心,直接当模板吧.先看POJ题目就知道重心什么意思了... 重心:删除该节点后最大连通块的节点数目最小 #include<cstdio> #include<cstring&g ...
- POJ3107--Godfather(树的重心)
vector建图被卡了..改为链式前向星500ms过的..差了四倍多?... 表示不太会用链表建图啊..自己试着写的,没看模板..嗯..果然错了..落了一句话orz 树的重心就是找到一个树中一个点,其 ...
随机推荐
- C++判断月份天数(判断闰年)
题目描述 输入年份和月份,输出这一年的这一月有多少天.需要考虑闰年. 输入格式 无 输出格式 无 输入输出样例 输入 #1 输出 #1 1926 8 31 输入 #2 输出 #2 2000 2 29 ...
- IT6563替代芯片|DP转HDMI 2.0音视频讯号转换芯片|CS5263Capstpne
IT6563替代芯片|CS 5263|4LAN DP转HDMI 2.0音视频讯号转换芯片IT6563是一款4LAN EDP转HDMI2.0 HDMI2.0转换器芯片.IT6563FN结合Display ...
- ret2dl_resolve
ret2dl_resolve是一种比较复杂的高级ROP技巧,利用它之前需要先了解动态链接的基本过程以及ELF文件中动态链接相关的结构. 我根据raycp师傅的文章,动手调试了一下: https://r ...
- 浅谈JVM内存分配与垃圾回收
大家好,我是微尘,最近又去翻了周志明老师的<深入理解Java虚拟机>这本书.已经看了很多遍了,每次都感觉似乎看懂了,但没过多久就忘了.这次翻了第三章的垃圾收集器与内存分配策略,感觉有了新的 ...
- 移动端的样式重置(CSS RESET)
/********** * reset *********/ * {box-sizing: border-box; -webkit-tap-highlight-color: rgba(0,0,0,0) ...
- (五)React Ant Design Pro + .Net5 WebApi:后端环境搭建-Autofac注入+ 泛型仓储
一. 简介 Autofac与.Net Core自带DI的区别,大佬级的文章数不胜数.我只是根据实际应用简单介绍(非常简单的那种) 1.批量注入,自带DI需要自己写循环反射注入,Autofac现成方法, ...
- 造轮子-strace(二)实现
这一篇文章会介绍strace如何工作,再稍微深入介绍一下什么是system call.再介绍一下ptrace.wait(strace依赖的system call).最后再一起来造个轮子,动手用代码实现 ...
- java运算符1
一:算术运算符(+, -, *, /, ++, --, ) 1.+号 :可以做加法运算(加号两边为字符和数字).正数表示 字符串连接符:只要+号两边其中有一边有字符串,输出时加号就充当连接 ...
- android-tools下的uiautomatorviewer截图,提示“Unexpected error while obtaining UI hierarchy”
使用原来工具下的uiautomatorviewer对android 5.1.0的手机,进行截图,是正常的,切换到android10截图,就报错"Unexpected error while ...
- epoll实现原理
作者:蓝形参链接:https://www.zhihu.com/question/20122137/answer/14049112来源:知乎 首先我们来定义流的概念,一个流可以是文件,socket,pi ...