HDU-6881 Tree Cutting

题意

\(n\) 个点的一棵树,要求删除尽量少的点,使得删点之后还是一棵树,并且直径不超过 \(k\),求删除点的数量

分析

补题之前的一些错误想法:

  1. 尝试将某条直径拎出来,然后贪心的找可以保留下来的点的最大个数(没办法保证删点之后的直径还在拎出来的那条路径上,另外如果在该路径上面尺取大概也不可做)
  2. 树的所有直径必然交于一点(当直径为奇数时可以是边上的一点)。问题转换为对于每个点求树上与其距离 \(\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 点分治)的更多相关文章

  1. hdu 5909 Tree Cutting [树形DP fwt]

    hdu 5909 Tree Cutting 题意:一颗无根树,每个点有权值,连通子树的权值为异或和,求异或和为[0,m)的方案数 \(f[i][j]\)表示子树i中经过i的连通子树异或和为j的方案数 ...

  2. 【HDU 5909】 Tree Cutting (树形依赖型DP+点分治)

    Tree Cutting Problem Description Byteasar has a tree T with n vertices conveniently labeled with 1,2 ...

  3. HDU 5909 Tree Cutting 动态规划 快速沃尔什变换

    Tree Cutting 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5909 Description Byteasar has a tree T ...

  4. hdu 5909 Tree Cutting——点分治(树形DP转为序列DP)

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5909 点分治的话,每次要做一次树形DP:但时间应该是 siz*m2 的.可以用 FWT 变成 siz*ml ...

  5. HDU 5909 Tree Cutting(FWT+树形DP)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5909 [题目大意] 给出一棵树,其每棵连通子树的价值为其点权的xor和, 问有多少连通子树的价值为 ...

  6. hdu 5909 Tree Cutting —— 点分治

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5909 点分治,每次的 rt 是必选的点: 考虑必须选根的一个连通块,可以DP,决策就是在每个子树中决定选不 ...

  7. HDU 5909 Tree Cutting

    传送门 题意: 有一棵n个点的无根树,节点依次编号为1到n,其中节点i的权值为vi, 定义一棵树的价值为它所有点的权值的异或和. 现在对于每个[0,m)的整数k,请统计有多少T的非空连通子树的价值等于 ...

  8. HDU.5909.Tree Cutting(树形DP FWT/点分治)

    题目链接 \(Description\) 给定一棵树,每个点有权值,在\([0,m-1]\)之间.求异或和为\(0,1,...,m-1\)的非空连通块各有多少个. \(n\leq 1000,m\leq ...

  9. HDU - 5909 Tree Cutting (树形dp+FWT优化)

    题意:树上每个节点有权值,定义一棵树的权值为所有节点权值异或的值.求一棵树中,连通子树值为[0,m)的个数. 分析: 设\(dp[i][j]\)为根为i,值为j的子树的个数. 则\(dp[i][j\o ...

随机推荐

  1. Map遍历的几种方式

    代码示例 /** * @author liaowenhui * @date 2020/6/25 11:15 */ public class TestMap { public static void m ...

  2. JPEG解码--(1)JPEG文件格式概览

    由于懒和人的忘性,以前做的一些笔记再回过头看时又有些生疏了,我决定把一些内容整理出来,以供有需要的来参考. 了解的人知道其价值所在,不知道的人就弃之如废物吧. 本篇是JPEG解码系列的第一篇--JPE ...

  3. 万字长文爆肝 DNS 协议!

    试想一个问题,我们人类可以有多少种识别自己的方式?可以通过身份证来识别,可以通过社保卡号来识别,也可以通过驾驶证来识别,尽管我们有多种识别方式,但在特定的环境下,某种识别方法可能比另一种方法更为适合. ...

  4. [GKCTF2020]老八小超市儿

    题目来自buu 一.题目初探 首先是一个shopxo搭建的演示站,通过扫描后台得到如下的网页 二.题目解答 首先是找到后台登陆的admin.php,然后通过百度找到shopxo的默认管理员登陆账号和密 ...

  5. VPS下环境漏洞部署

    No.1 声明 1.由于本环节运行在公网,如何同样复现情况,复现成功后请立即关闭环境! 2.本环境仅用于漏洞复现! No.2 安装docker curl -s https://get.docker.c ...

  6. python中IF语句容易犯的错误CASE

    python中没有switch   case类似的语句,但是下面的IF语句却与之类似,却又不同: A = B = C = D = E = 1 if A == 1: B=2 elif B ==2: C= ...

  7. kafka(三)原理剖析

    一.生产者消息分区机制原理剖析 在使用Kafka 生产和消费消息的时候,肯定是希望能够将数据均匀地分配到所有服务器上.比如很多公司使用 Kafka 收集应用服务器的日志数据,这种数据都是很多的,特别是 ...

  8. oracle ORA-00060死锁查询、表空间扩容

    --查看被锁住的表 select b.owner,b.object_name,a.session_id,a.locked_mode from v$locked_object a,dba_objects ...

  9. mastercam2018安装教程

    安装前先关闭杀毒软件和360卫士,注意安装路径不能有中文,安装包路径也不要有中文. [安装环境]:Win7/Win8/Win10 1.选中[Mastercam2018]压缩包,鼠标右击选择[解压到Ma ...

  10. django ajax应用

    ajax: 什么是ajax,有什么作用: 以前我们在页面向后台提交数据的时候都是使用from表单,这样的提交会在提交的时候将整个页面全部刷新,如果你在填写表单的时候提交之后发现某个数据不对,但是你已提 ...