题目链接

牛妹有一张连通图,由n个点和n-1条边构成,也就是说这是一棵树,牛妹可以任意选择一个点为根,根的深度deprootdep_{root}deproot​​为0,对于任意一个非根的点,我们将他到根节点路径上的第一个点称作他的父节点,例如1为根,1−41-41−4的;路径为1−3−5−41-3-5-41−3−5−4时,4的父节点是5,并且满足对任意非根节点,depi=depfai+1dep_i=dep_{fa_i}+1depi​=depfai​​+1,整棵树的价值 W=∑i=1ndepiW=\sum\limits_{i=1}^n dep_iW=i=1∑n​depi​ ,即所有点的深度和 牛妹希望这棵树的W最小,请你告诉她,选择哪个点可以使W最小

输入描述:

第一行,一个数,n接下来n-1行,每行两个数x,y,代表x-y是树上的一条边

输出描述:

一行,一个数,最小的W

数据范围 1(30pts)1(30pts)1(30pts): 每次换根dfs,求出对于每个点为根时的w,取min即可,复杂度Θ(n2)\Theta(n^2)Θ(n2)
数据范围 2(100pts)2(100pts)2(100pts): 考虑任意选择一个点为根算出KrootK_{root}Kroot​,对于两个相邻的点a,ba,ba,b,我们令sizasiz_asiza​​表示bbb为根时,aaa的子树大小,sizbsiz_bsizb​为aaa为根时,bbb的子树大小,那么显然在a,ba,ba,b之间转移答案的变化为∣siza−sizb∣|siz_a-siz_b|∣siza​−sizb​∣,故单次转移的复杂度为O(1)O(1)O(1),总复杂度O(n)O(n)O(n)

两种方法

注意这两种方法都必须用C++14才能AC

法1:重心法

不懂重心的点这里
先用树形DP找到树的重心,再BFS以重心为根求深度和即可

#include <bits/stdc++.h>
#define inf 100000000
#define x first
#define y second
#define pp pair<ll, ll>
using namespace std;
typedef long long ll;
const ll N = 2e6+5;
ll head[N], top = 0;
ll n;
ll son[N], vis[N];
ll ans, res, point;
struct Edge
{ll v, next;
}edge[N * 2];void init()
{memset(head, -1, sizeof(head));top = 0;memset(son, 0, sizeof(son));ans = inf;
}void addedge(ll u, ll v)
{edge[top].v = v;edge[top].next = head[u];head[u] = top++;
}void dfs(ll u, ll fa)
{son[u] = 1;ll Max = 0;for (ll i = head[u]; i != -1; i = edge[i].next){ll v = edge[i].v;if (v == fa)continue;dfs(v, u);son[u] += son[v];Max = max(Max, son[v]);}ll tmp = max(Max, n - son[u]);if (tmp < ans || tmp == ans && u < point)//如果相等说明有两个重心,选编号最小的那一个{ans = tmp;point = u;}
}ll bfs()
{queue<pp> q;vis[point] = 1;//重心q.push(make_pair(point, 0));//点和深度while (!q.empty()){pp now = q.front();q.pop();for (ll i = head[now.x]; i != -1; i = edge[i].next){ll to = edge[i].v;if (!vis[to]){vis[to] = 1;q.push(make_pair(to, now.y + 1));res += now.y + 1;//求的是总的深度和,所以每“递归”一次就加上+1后的深度}}}
}int main()
{init();scanf("%lld", &n);for (ll i = 1; i < n; i++){ll u, v;scanf("%lld%lld", &u, &v);addedge(u, v);addedge(v, u);}dfs(1, -1);bfs();printf("%lld\n", res);return 0;
}

法2:换根法

先以1为根求深度和,再BFS换根,求最小值即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
int head[maxn], to[maxn<<1], nex[maxn<<1], tot;
ll dep[maxn];
int son[maxn], n;
ll ans;int read()
{int x=0;char s=getchar();for( ;isdigit(s);x=x*10+s-48,s=getchar());return x;
}void init()
{memset(head, -1, sizeof(head));tot = 0;
}
void add( int x, int y )
{to[tot] = y;nex[tot] = head[x];head[x] = tot++;
}
void dfs( int x, int fx, int deep )
{for ( int i = head[x]; ~i; i = nex[i] ){int v = to[i];if(v == fx) continue;dfs(v, x, deep + 1);son[x] += son[v];dep[x] += dep[v];}son[x]++, dep[x] += deep;
}struct node{int x, fx;
}p[maxn];int cnt = 0;
int vis[maxn];void bfs()
{queue<node> que;vis[1] = 1;for ( int i = head[1]; ~i; i = nex[i] ){int v = to[i];que.push({v, 1});vis[v] = 1;}while ( !que.empty() ){node now = que.front(); que.pop();p[cnt].x = now.x; p[cnt++].fx = now.fx;int u = now.x;for ( int i = head[u]; ~i; i = nex[i] ){int v = to[i];if(vis[i]||v == now.fx) continue;vis[i] = 1;node t;t.x = v; t.fx = u;que.push(t);}}for ( int i = 0; i < cnt; i++ ){int x = p[i].x, fx = p[i].fx;ll tmp = dep[x];tmp -= son[x];tmp += dep[fx] - dep[x] + (n - son[x]);dep[x] = tmp;ans = min(ans, tmp);}
}int main()
{init();n=read();for ( int i = 1; i <= n - 1; i++ ){int a=read(), b=read();add(a, b); add(b, a);}dfs(1, 1, 0);ans = dep[1];bfs();printf("%lld\n", ans);return 0;
}

有任何疑问欢迎评论哦虽然我真的很菜

牛客 Tree(最小深度总和)(两种方法求重心)难度⭐⭐⭐相关推荐

  1. python创建树结构、求深度_Python实现二叉树的最小深度的两种方法

    找到给定二叉树的最小深度 最小深度是从根节点到最近叶子节点的最短路径上的节点数量 注意:叶子节点没有子树 Example: Given binary tree [3,9,20,null,null,15 ...

  2. python实现人脸口罩检测(基于opencv和深度学习两种方法)

    人脸口罩检测GUI系统(基于opencv和深度学习两种方法对比) 由于疫情的影响,人脸口罩检测系统的开发成为很多人争相开发的一种算法.很多公司或者个人都开源了他们很多的代码或者SDK.大家在GitHu ...

  3. C++求二叉树深度的两种方法

    今天在leetcode中碰到了求二叉树的深度问题,于是总结一下这两种方法 方法一是用递归的方法,方法二是借助队列和层序遍历的思想 #include<iostream> #include&l ...

  4. HDU Problem - 6214 Smallest Minimum Cut(最小割边,两种方法)

    题目链接 Problem Description Consider a network G=(V,E)G=(V,E)G=(V,E) with source sss and sink t" r ...

  5. 两种方法求最大公约数和最小公倍数

    #include<iostream> using namespace std;// 最小公倍数 = 两数相乘/最大公约数 /* //辗转相除法 int gcd(int a, int b){ ...

  6. C语言两种方法求圆的面积与周长编程

    方法一:程序如下: #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() {const float pi = 3.14; ...

  7. 求逆元的两种方法+求逆元的O(n)递推算法

    到国庆假期都是复习阶段..所以把一些东西整理重温一下. gcd(a,p)=1,ax≡1(%p),则x为a的逆元.注意前提:gcd(a,p)=1; 方法一:拓展欧几里得 gcd(a,p)=1,ax≡1( ...

  8. 联想G510进入BIOS的两种方法

    按照网上的说法,FN+F2可以进入BIOS,但是自己反复试了半天还是不行,于是咨询客服.给了两种方法进入BIOS. 1.开机或重启是按住FN,然后不停地敲击F2,注意是不停地,敲一次或一直按着没什么卵 ...

  9. 使用Linux服务器运行深度学习代码 || 两种方法,粗暴高效

    [本文是介绍快速远程使用Linux服务器运行深度学习.机器学习代码,全程高能,史上最全] 前面介绍了以下几个内容,不会的可以转链接看下 教你无脑式安装Xshell.Xftp,快速远程连接使用Linux ...

最新文章

  1. new file https 找不到路径_Python3用pathlib模块替代os.path进行文件路径的操作
  2. 同居1月 VS 同居1年,太真实了...
  3. Java中三种交换值得方式
  4. 模块和包——Python
  5. 自定义异步加载资源插件
  6. bat 设置变量带中文
  7. python FTPS使用ftplib下载文件(详细)
  8. 【JavaScript联系练习】实现一个打点计时器
  9. 海马体启发的记忆模型
  10. jqprint插件打印去掉页眉页脚的方式
  11. Android视频播放器开发
  12. (3种解决思路)OSError: [Errno 22] Invalid argument:解决python爬虫中报错
  13. 电子技术课程设计-正弦波发生及频率显示电路-电子线路CAD原理图
  14. 剑灵M双端一键端开服端
  15. yii rules最全规则
  16. 经纬恒润测试开发面经
  17. 把视频抠像放到另一个视频上怎么做?这几种方法很简单
  18. 编译linux内核成vmlinuz,Linux内核编译与安装
  19. 【感想】关于“加班”的看法
  20. insert overwrite table不会覆盖原文件,而是新增一个文件

热门文章

  1. 实战:基于深度学习和几何的3D边界框估计
  2. 编写同时在PyTorch和Tensorflow上工作的代码
  3. 将Centos的yum源更换为国内的阿里云源
  4. ZEGO即构科技携小程序连麦直播方案亮相GMIC
  5. Mybaits整合Spring自动扫描 接口,Mybaits配置文件.xml文件和Dao实体类
  6. 公开课视频-《第04章 部署-Microsoft-服务器虚拟化-Hyper-V 2012 R2》
  7. 树的分类,特性与遍历
  8. winsock2之最简单的win socket编程
  9. 项目松弛时期 团队如何休养生息?
  10. java 提取内容并排序