HDU-6881 Tree Cutting (HDU多校D10T5 点分治)
HDU-6881 Tree Cutting
题意
\(n\) 个点的一棵树,要求删除尽量少的点,使得删点之后还是一棵树,并且直径不超过 \(k\),求删除点的数量
分析
补题之前的一些错误想法:
- 尝试将某条直径拎出来,然后贪心的找可以保留下来的点的最大个数(没办法保证删点之后的直径还在拎出来的那条路径上,另外如果在该路径上面尺取大概也不可做)
- 树的所有直径必然交于一点(当直径为奇数时可以是边上的一点)。问题转换为对于每个点求树上与其距离 \(\lfloor {k\over 2} \rfloor\) 的点的个数。尝试树上DP,或者差分乱搞,只能计算出每个点子树上的,并不完整,有想过点分治,但是没有想清楚要如何处理。
开始讲正解:
考虑点分治,假设当前处理的根是 \(rt\),那么很容易算出根的答案,如何计算子树内的?假设 \(rt\) 有一颗子树 \(u\) ,在计算 \(u\) 中的点 \(x\) 的答案时,不需要考虑 \(u\) 中除 \(x\) 外的其他点对它的贡献,这一部分会继续分治下去求解。现在只需要计算 \(rt\) 除去 \(u\) 的点对 \(x\) 的贡献即可。
由于树边没有权值,我们可以用深度 \(dep\) 来表示树上路径距离,在求 \(x\) 的答案时,假设直径为 \(k\) (先假设\(k\)是偶数), 那么也就是求除去 \(u\) 这颗子树,其他深度大于 \((k/2) - dep[x]\) 的点的个数。可以先预处理出来 \(rt\) 的深度数组,然后求解 \(u\) 时,在预处理出来一个深度数组 \(c\),两者做差即可。
设子树 \(u\) 旁边的子树 \(v\) 上的一点 \(y\) , 那么当 \(dep[y] + dep[x] > k/2\), 才需要累计 \(y\) 对 \(x\)的贡献
当 \(k\) 为奇数时,树的直径中点在边上,我们考虑 \(x\) 到 \(anc[x]\) 这条边,答案应该是深度大于 \((k-1)/2-dep[u]+1\)的点的个数。但是如果 \(anc[x]\) 是 \(rt\),要注意还要累计\(x\) 所在子树 \(u\) 上的贡献。因为该边并不会递归下去分治求解。
const int N = 300000 + 5;
typedef pair<int,int> pii;
vector<pii> g[N];
int n, k;
int anc[N], sz[N], dep[N], allcount[N], o[N];
int cnt_node[N], cnt_edge[N];
bool del[N];
int rt, min_size, all, max_dep, node_len, edge_len;
void getsz(int x, int fa) {
anc[x] = fa;
sz[x] = 1;
int maxpart = 0;
for(auto t : g[x]) {
if(t.first == fa || del[t.first]) continue;
getsz(t.first, x);
sz[x] += sz[t.first];
maxpart = max(maxpart, sz[t.first]);
}
maxpart = max(maxpart, all - sz[x]);
if(maxpart < min_size) {
min_size = maxpart; rt = x;
}
}
// 获得点分治当前处理树的dep数组,与后缀和 allcount
void travel(int u) {
static int o[N];// BFS队列
int l = 0, r = -1;
o[++r] = u;
while(l <= r) {
int u = o[l++];
allcount[dep[u]] ++;
max_dep = max(max_dep, dep[u]);
for(auto &v : g[u]){
if(v.first == anc[u] || del[v.first]) continue;
anc[v.first] = u;
dep[v.first] = dep[u] + 1;
o[++r] = v.first;
}
}
for(int i=max_dep-1;i>=0;i--) allcount[i] += allcount[i+1];
}
void travel2(int u) {
static int c[N], o[N];
int l = 0, r = -1;
o[++r] = u;
while(l <= r) {
int u = o[l++];
c[dep[u]] ++;
for(auto &v:g[u]) {
if(del[v.first] || v.first == anc[u]) continue;
o[++r] = v.first;
}
}
int Max = dep[o[r]];
for(int i=Max - 1; i >= 0; i--) c[i] += c[i+1];
for(int i=0;i<=r;i++){
int u = o[i];
int d = max(0, node_len - dep[u] + 1);
cnt_node[u] += allcount[d] - c[d];
// 下面是处理树直径中心在边上的情况
d = max(0, edge_len - dep[u] + 2);
for(auto &v:g[u]) {
if(v.first != anc[u]) continue;
cnt_edge[v.second] += allcount[d] - c[d];
if(anc[u] == rt) { // 如果是与rt相连的边,要统计 c 的答案
cnt_edge[v.second] += c[edge_len + 2];
}
}
}
// 最后要记得清空
for(int i=0;i<=Max;i++) c[i] = 0;
}
void get(int u) {
del[u] = 1;
dep[u] = 0;
max_dep = 0;
travel(rt);
cnt_node[u] += allcount[node_len + 1];
for(auto &v : g[u]) {
if(del[v.first]) continue;
travel2(v.first);
}
_rep(i,0,max_dep) allcount[i] = 0;
for(auto &v : g[u]) {
if(del[v.first]) continue;
all = sz[v.first]; min_size = all;
getsz(v.first, 0);
getsz(rt, 0);
get(rt);
}
}
int main(){
int T; scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &k);
node_len = k / 2;
edge_len = (k - 1) / 2;
_rep(i, 1, n) {
g[i].clear();
cnt_edge[i] = cnt_node[i] = del[i] = 0;
}
_rep(i, 1, n-1) {
int u, v; scanf("%d%d", &u,&v);
g[u].push_back(pii(v, i));
g[v].push_back(pii(u, i));
}
all = n; min_size = n;
getsz(1, 0);
getsz(rt, 0);
get(rt);
int res = n;
_rep(i, 1, n) res = min(res, cnt_node[i]);
_rep(i, 1, n-1) res = min(res, cnt_edge[i]);
printf("%d\n", res);
}
return 0;
}
HDU-6881 Tree Cutting (HDU多校D10T5 点分治)的更多相关文章
- hdu 5909 Tree Cutting [树形DP fwt]
hdu 5909 Tree Cutting 题意:一颗无根树,每个点有权值,连通子树的权值为异或和,求异或和为[0,m)的方案数 \(f[i][j]\)表示子树i中经过i的连通子树异或和为j的方案数 ...
- 【HDU 5909】 Tree Cutting (树形依赖型DP+点分治)
Tree Cutting Problem Description Byteasar has a tree T with n vertices conveniently labeled with 1,2 ...
- HDU 5909 Tree Cutting 动态规划 快速沃尔什变换
Tree Cutting 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5909 Description Byteasar has a tree T ...
- hdu 5909 Tree Cutting——点分治(树形DP转为序列DP)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=5909 点分治的话,每次要做一次树形DP:但时间应该是 siz*m2 的.可以用 FWT 变成 siz*ml ...
- HDU 5909 Tree Cutting(FWT+树形DP)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5909 [题目大意] 给出一棵树,其每棵连通子树的价值为其点权的xor和, 问有多少连通子树的价值为 ...
- hdu 5909 Tree Cutting —— 点分治
题目:http://acm.hdu.edu.cn/showproblem.php?pid=5909 点分治,每次的 rt 是必选的点: 考虑必须选根的一个连通块,可以DP,决策就是在每个子树中决定选不 ...
- HDU 5909 Tree Cutting
传送门 题意: 有一棵n个点的无根树,节点依次编号为1到n,其中节点i的权值为vi, 定义一棵树的价值为它所有点的权值的异或和. 现在对于每个[0,m)的整数k,请统计有多少T的非空连通子树的价值等于 ...
- HDU.5909.Tree Cutting(树形DP FWT/点分治)
题目链接 \(Description\) 给定一棵树,每个点有权值,在\([0,m-1]\)之间.求异或和为\(0,1,...,m-1\)的非空连通块各有多少个. \(n\leq 1000,m\leq ...
- HDU - 5909 Tree Cutting (树形dp+FWT优化)
题意:树上每个节点有权值,定义一棵树的权值为所有节点权值异或的值.求一棵树中,连通子树值为[0,m)的个数. 分析: 设\(dp[i][j]\)为根为i,值为j的子树的个数. 则\(dp[i][j\o ...
随机推荐
- [Leetcode刷题]——链表
一.找出两个链表的交点 160.相交链表(easy)2021-01-05 编写一个程序,找到两个单链表相交的起始节点 如下面的两个链表,在c1 处相交: public class Soluti ...
- 从零开始的C程序设计大作业——学生成绩管理系统
前言 学生成绩管理系统可以说是C语言程序设计的结课的必备大作业了.花了些时间,费了些头发肝了下,完成了两个系统,一个是控制台版本的,另一个用easyx图形库进行了优化. 先放出完成后的演示图片占个坑. ...
- web元素定位和appium-app元素定位
一.web页面元素定位工具介绍 1.打开google浏览器,按F12进入开发者模式,如下图: 2.用鼠标点击下图红色框中的箭头--然后鼠标移动到web页面的元素上(此处为百度框),会自动定位到对应的h ...
- Spring框架之websocket源码完全解析
Spring框架之websocket源码完全解析 Spring框架从4.0版开始支持WebSocket,先简单介绍WebSocket协议(详细介绍参见"WebSocket协议中文版" ...
- 编译安装PHP - 7.3.16
编译安装PHP - 7.3.16 1 ) 安装依赖包: yum install -y gcc gcc-c++ make zlib zlib-devel pcre pcre-devel libjpeg ...
- 【排序基础】1、选择排序法 - Selection Sort
文章目录 选择排序法 - Selection Sort 为什么要学习O(n^2)的排序算法? 选择排序算法思想 操作:选择排序代码实现 选择排序法 - Selection Sort 简单记录-bobo ...
- 【数据库】MySQL & SQL 介绍
文章目录 MySQL & SQL 介绍 1.MySQL的背景 2.MySQL的优点 3.MySQL的安装 4.MySQL服务的启动和停止 方式一 方式二 5.MySQL服务的登录和退出 方式一 ...
- 避免用using包装DbContext【翻译】
EF和EF Core 的DbContext类实现IDisposable接口.因此,很多最佳编程实践中都建议你将它们放在一个using()块中.不幸的是,至少在Web应用程序中,这样做通常不是一个好主意 ...
- CVE-2018-1273 Spring Data Commons 远程命令执行漏洞复现
一.漏洞描述 Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,Spring Data Commons是Spring Data下所有子项目共享的基础框架.Spring Data ...
- git 基本命令和操作
设置全局用户名+密码 $ git config --global user.name 'runoob' $ git config --global user.email test@runoob.com ...