题目链接


题目大意:

给定nnn个节点的树,问删除尽可能小的点使得树的直径不超过KKK,输出最小删除的点数,(1<=k<=n<=3e5)(1<=k<=n<=3e5)(1<=k<=n<=3e5)


解题思路:

这道题和AGC001的c题是一模一样的,但是这道题数据量比较大,但是核心思想还是不变的还是,枚举每个点作为直径的中间点,或者枚举边作为中心边,但是AGC001的c题数据量比较小可以暴力枚举这个题有3e53e53e5,我们应该考虑如何优化暴力。

对于树上统计问题我们最先想到的就是点分治。

1.对于KKK是偶数的情况:因为KKK是偶数所以直径上的点就只有奇数个,那么对于每一个点uuu我们假设这个点是直径的中点,那么距离uuu长度大于k/2k/2k/2的都要删掉,
那么每次就取重心,重心和孩子分别考虑;
     1.1对于每个点uuu我们考虑和点uuu不在同一颗子树里面的点对这个点的影响,在同一个子树里面的点通过分治会递归下去求解。
     1.2首先我们先整个分治的树求一共深度计数num[](就是统计一下各个深度的点有多少个)num[](就是统计一下各个深度的点有多少个)num[](就是统计一下各个深度的点有多少个)。
     1.3对于每个子树求解答案的时候,我们先对这个子树求出属于它的num1[]num1[]num1[],numnumnum和num1num1num1做差就是其他子树的影响了。
     1.4为了快速减我们可以求个后缀和。因为是大于K/2K/2K/2都要减掉

2.对于KKK是奇数的情况:因为KKK奇数那么中心位置是一个边,那么我们的答案就是枚举边做直径的中点,对于距离当前边的距离大于等于k/2+1k/2+1k/2+1的点均要去掉。
     2.1:注意一点就是我们点分治的时候是枚举点,但是统计答案的时候是边。你从父节点到下一个分治重心的时候,过渡的边(root−>son)(root->son)(root−>son)会只统计其他子树对这条边的影响,所以我们要为这些边直接加上自己子树上大于等于K/2+1K/2+1K/2+1的点的个数。


#pragma comment(linker,"/STACK:102400000,102400000")
#pragma GCC optimize(2)
#include <bits/stdc++.h>using namespace std;
typedef long long ll;
const int maxn = 3e5 + 10;
struct node {int nxt, to;
}edge[maxn<<1];
int head[maxn], cnt;
int n, k;
inline void add(int from, int to) {edge[++cnt] = {head[from],to};head[from] = cnt;
}
//..........
bool vis[maxn];
int MX, root, max_son[maxn], now_num ,siz[maxn];
void getroot(int u, int fa) {max_son[u] = 0;siz[u] = 1; for(int i = head[u]; i; i = edge[i].nxt) {int v = edge[i].to;if(v == fa || vis[v]) continue;getroot(v,u);siz[u] += siz[v];max_son[u] = max(max_son[u],siz[v]);}max_son[u] = max(max_son[u],now_num - siz[u]);if(max_son[u] < MX) MX = max_son[u], root = u;
}
//..................................
int num[maxn], max_depth;
int res[maxn];
void dfs1(int u, int  fa, int depth) {//对整个树dfsnum[depth] ++;//深度计数max_depth = max(max_depth,depth);for(int i = head[u]; i; i = edge[i].nxt) {int v = edge[i].to;if(vis[v] || v == fa) continue;dfs1(v,u,depth+1);}
}
int num1[maxn], max_depth1;
void dfs2(int u, int fa, int depth) {//对某颗子树dfsnum1[depth]++;max_depth1 = max(max_depth1,depth);for(int i = head[u]; i; i = edge[i].nxt) {int v = edge[i].to;if(v == fa || vis[v]) continue;dfs2(v,u,depth+1);}
}void dfs3(int u, int fa, int depth, int cntedge) {//统计答案的dfsif(k & 1) {//如果是边是中心int D = max(0,(k/2+1)-depth+1);res[cntedge] += num[D] - num1[D];if(fa == root) ///当前边是和root连的边时,要算子树u中对当前边的贡献res[cntedge] += num1[k/1+1+1];///之所以后者要+1,时是因为要从当前边开始往下找,而当前边深度对于root来说一定是1;} else {//点是中心int D = max(0,k/2+1-depth);res[u] += num[D] - num1[D];}for(int i = head[u]; i; i = edge[i].nxt) {int v = edge[i].to;if(v == fa || vis[v]) continue;dfs3(v,u,depth+1,i/2);}}void Div(int u) {vis[u] = 1;max_depth = 0;dfs1(u,0,0);for(int i = max_depth-1; i >= 0; -- i) num[i] += num[i + 1];if(k % 2 == 0) res[u] += num[k/2+1]; //跟新分治节点的答案for(int i = head[u]; i; i = edge[i].nxt) {int v = edge[i].to;if(vis[v]) continue;max_depth1 = 0;dfs2(v,u,1);   //计算当前子树大小for(int j = max_depth1-1; j >= 0; -- j) num1[j] += num1[j + 1];dfs3(v,u,1,i/2);for(int j = max_depth1; j >= 0; -- j) num1[j] = 0;}for(int i = max_depth; i >= 0; -- i) num[i] = 0;for(int i = head[u]; i; i = edge[i].nxt) {int v = edge[i].to;if(vis[v]) continue;MX = 1e9, root = 0;now_num = siz[v];getroot(v,u);Div(root);}
}inline void init() {cnt = 1;for(int i = 0; i <= n; ++ i) vis[i] = 0, res[i] = 0, num[i] = 0, head[i] = 0;
}int main() { int T;scanf("%d",&T);while(T --) {scanf("%d%d",&n,&k);init();for(int i = 0; i < n - 1; ++ i) {int l, r;scanf("%d%d",&l,&r);add(l,r);add(r,l);}root = 0, MX = 1e9;now_num = n;getroot(1,0);Div(root);int ans = 1e9;for(int i = 1; i < n; ++ i)ans = min(ans,res[i]);if(k % 2 == 0) ans = min(ans,res[n]);//因为边只有n-1条,但是点却又n条。printf("%d\n",ans);}return 0;
}

点分治问题 ----------- HDU6881 Tree Cutting or 2020杭电多校第10场 [点分治+思维]相关推荐

  1. 2020杭电多校第六场 A Very Easy Math Problem 莫比乌斯反演 (HDU 6833)

    A Very Easy Math Problem 题解 ∑ a 1 = 1 n ∑ a 2 = 1 n ⋅ ⋅ ⋅ ∑ a x = 1 n ( ∏ j = 1 x a j k ) f ( g c d ...

  2. 2020 杭电多校5 1007、1008、1011

    1007 tree (hdu 6820) 题意: 输入 n,kn,kn,k ,给定一棵带权树,包含 nnn 个节点,要求找到一个权重和最大的子树,使得这个子树度数大于 kkk 的节点不超过 111 个 ...

  3. 2020杭电多校训练(第五、六场)

    目录 第五场 1001.Tetrahedron 1009.Paperfolding 1003.Boring-Game 1012.Set1 1007.Tree 第六场 1006.A-Very-Easy- ...

  4. 欧拉降幂 ---- 2020 杭电多校[E - Fibonacci Sum]+欧拉降幂+和式的调整+二次剩余+毒瘤卡常

    解题思路: 首先你得知道斐波那契的通项式子:首先你得知道斐波那契的通项式子:首先你得知道斐波那契的通项式子: F(n)=15[(1+52)n−(1−52)n]F(n) = {1\over\sqrt5} ...

  5. 2020杭电多校(二) New Equipments(最小费用最大流)

    New Equipments 思路 数据已经有提示了b∗b<=4∗a∗cb * b <= 4 * a * cb∗b<=4∗a∗c,这意味着,每一个a,b,ca, b, ca,b,c构 ...

  6. 2020杭电多校训练(第一、二场)

    目录 第一场 1005.Fibonacci-Sum 1009.Leading-Robots 1006.Finding-a-MEX 第二场 1012.String-Distance 1005.New-E ...

  7. 【2020杭电多校】 Lead of Wisdom 【暴搜】

    这道题,我还以为是什么状压dp(看到dp头就疼)? 我是不是脑瘫了,一开始想过暴搜,就这?最坏时间复杂度跑一遍早就超过1e8了,一秒肯定跑不完,能过?弃了弃了,谁知道题目给了8000ms!!! 什么是 ...

  8. 2020杭电多校第二场 Lead of Wisdom(爆搜)

    Problem Description In an online game, "Lead of Wisdom" is a place where the lucky player ...

  9. 2020杭电计算机考研复试面试

    2020杭电计算机考研复试面试问题 20考研学渣一枚,有幸上岸,整理了下自己以及朋友复试遇到的问题 英语题目: 1.如果你想邀请以为古代名人参加你的聚会,你会选谁 2.最重要的品质,为什么 专业题: ...

最新文章

  1. Java8中Lambda表达式的10个例子
  2. 使用Notepad++比较文件的差异
  3. TCP实现服务器与单客户端连接(多线程)
  4. 让用户用最短的时间接受 Confluence
  5. u-boot内核启动分析
  6. centos8 配置 dns_广电行业DNS、DHCP案例详解
  7. configurations in Gateway hub system
  8. 正反对角线java表示_连接4对角线Win Check
  9. 如何使用svn客户端
  10. “叔叔,你来监考了!”
  11. 计算机学院边浩东,热烈祝贺建设信息工程系学生在“新华三杯”全国大学生数字技术大赛上取得优异成绩...
  12. 一个手机用c网可以打开网站切换到g网就打不开_推荐一些设计师常用网站!!!...
  13. 儿童专注力训练之数图形
  14. 即将创业的我转发一篇鸡汤文---采访了 10 位身价过亿的 CEO,我终于看懂了有钱人的“奋斗”
  15. 极米H5值得入手吗?极米H5实际体验如何?画面对比实测
  16. linux查看raid情况工具,使用MegaCli工具查看Raid磁盘阵列状态
  17. laydate自定义开始日期和结束日期
  18. 将js预编译熟稔于心
  19. 0083-Zipkin耗时分析
  20. UINavigationController导航控制器详解

热门文章

  1. 爬虫之requests模块发送post请求
  2. 计算机网络分类(按覆盖范围分类)“名称、范围”
  3. 吴恩达:关于机器学习职业生涯以及阅读论文的一些建议
  4. pip经常的使用技巧
  5. 接口测试用例测试模板
  6. ERP实施完了,为什么还要做MES?
  7. 对《软件工程》第四章的理解
  8. 【转】Linux世界驰骋——文件系统和设备管理
  9. OMG!我居然把新首页设计出来了!
  10. 《网管员必读》学习笔记之DNS服务器的安装与配置