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 ...
 
随机推荐
- 20210107 - python 的Excel自动化
			
1.前置条件:用python, 开发工具pycharm 2.安装包: pip install openpyxl, 该包支持:xlsx, 但是不支持xlrd.xlwt格式的文件 3.概念介绍: 一个e ...
 - 结合MATLAB、Python、R语言,在求得显著差异的边(节点对)之后,怎么画circle图
			
先来看看成果图: OK,开始画图: 实验背景声明:在脑影像分析中,我们首先构建脑网络,然 ...
 - M43 第一阶段考试
			
一.解答题 1.统计当前主机的TCP协议网络各种连接状态出现的次数 netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a ...
 - 【EXPDP】指定导出,只导出函数,导出的时候加上where条件过滤
			
expdp导出的时候可以使用parfile这个参数,在parfile中添加想要的导出信息: 这里简单写了几句: vim test.par include=function --导出函数 inc ...
 - linux搭建简单samba服务器
			
1.安装需要的软体 yum install -y samba samba-client samba-common 2.创建samba需要的本地用户,创建samba服务使用的目录 Linux系统文件的读 ...
 - AQS之ReentrantReadWriteLock精讲分析上篇
			
1.用法 1.1 定义一个安全的list集合 public class LockDemo { ArrayList<Integer> arrayList = new ArrayList< ...
 - winform 添加背景图 闪屏问题解决
			
winform中只要添加了背景图片资源,窗体加载显示的时候就会出现不停的闪屏操作,网上找了很多方法,效果都不明显: 然后自己观察和思路:看窗体的加载过程,当有背景图的时候,首先出来的是背景图,之后背景 ...
 - linux驱动设备号
			
一.设备号基础 一般来说,使用ls -l命令在时间一列的前一列的数字表示的是文件大小,但如果该文件表示的是一个设备的话,那时间一列的前一列将有两个数字,用逗号分隔开,如下图: 前一个数字表示主设备号, ...
 - ES入门及安装软件
			
es介绍 Elasticsearch,简称es,是一款高扩展的分布式全文检索引擎.它可以近乎实时的存储,检索数据.es是面向文档型的数据库,一条数据就是一个文档,用json做为文档序列化的格式.es是 ...
 - JavaScript 实现排序算法
			
参考文章: 十大经典排序算法动画,看我就够了! 1. 冒泡排序 思路 比较所有相邻元素,如果第一个比第二个大,则交换它们 一轮下来,可以保证最后一个数是最大的 执行n-1轮,就可以完成排序 代码 Ar ...