当前位置: 首页 > wzjs >正文

以下可以制作二维码的网站为网络推广团队

以下可以制作二维码的网站为,网络推广团队,网站建设和优化的营销话术,二级网站排名做不上去目录 题目算法标签: d f s dfs dfs, 树的重心, 倍增优化, 换根 d p dp dp思路详细注释代码精简注释代码 题目 1157. 树的重心 算法标签: d f s dfs dfs, 树的重心, 倍增优化, 换根 d p dp dp 思路 对于一棵树来说, 树的重心只有一个或者两个, 问题就是枚举删除每一条边,…

题目

1157. 树的重心
在这里插入图片描述

算法标签: d f s dfs dfs, 树的重心, 倍增优化, 换根 d p dp dp

思路

对于一棵树来说, 树的重心只有一个或者两个, 问题就是枚举删除每一条边, 算这两个数的所有重心的编号之和

如果根节点不是重心, 那么必然存在一个子树的点数 > n 2 > \frac{n}{2} >2n, 而且只存在一个子树的节点个数 > n 2 > \frac{n}{2} >2n

在这里插入图片描述

如何寻找重心?
按照 > n 2 > \frac{n}{2} >2n的子树走最后一个位置, 也就是说所有儿子的点数都严格小于 ≤ n 2 \le \frac{n}{2} 2n, 当前点一定是重心, 也就是重心是一定存在的

在这里插入图片描述
如何判断树中是否存在两个重心?
假设红色节点是重心. 那么有红色方向点数 ≤ n 2 \le \frac{n}{2} 2n,并且蓝色区域点数是 ≥ n 2 \ge \frac{n}{2} 2n, 如果蓝色点也是重心, 那么是相反的情况, 最终推出每个区域点数必须严格等于 n 2 \frac{n}{2} 2n(要求总点数是偶数), 并且两个重心是相邻的

在这里插入图片描述
在寻找 v v v子树重心的过程中时候可以使用倍增进行优化
在这里插入图片描述
但是上半部分如何计算?
u u u部分换根换到 v v v, 将上面部分换位以 v v v为根, 换完根之后只有 u u u节点自己会变, s [ u ] = n − s [ v ] s[u] = n - s[v] s[u]=ns[v], f [ u ] f[u] f[u] u u u的新的重儿子是否是 v v v, 如果是 v v v那么需要更换为除了 v v v之外的重儿子

详细注释代码

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;typedef long long LL;
const int N = 300010, M = N * 2, K = 19; // K是倍增数组的层数int n;
int h[N], e[M], ne[M], idx; // 邻接表存图
// f[u][k]表示从u节点出发,沿着重链走2^k步到达的节点
// g是f数组的备份,用于回溯
// p记录父节点,sz记录子树大小
int p[N], f[N][K], g[N][K], sz[N];
LL ans; // 存储最终答案// 添加边
void add(int a, int b) {e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}// 计算倍增数组,u是当前节点,son是u的重儿子
void calc_st(int u, int son) {f[u][0] = son; // 第一步走重儿子for (int i = 1; i < K; i++)f[u][i] = f[f[u][i - 1]][i - 1]; // 倍增思想
}// 计算以u为根的子树的重心,并累加到答案中
void calc_ans(int u) {int tot = sz[u]; // 当前子树的总大小// 从高位到低位尝试走,找到最接近子树中间位置的节点for (int i = K - 1; i >= 0; i--)if (f[u][i] && tot - sz[f[u][i]] <= tot / 2)u = f[u][i];// 累加找到的重心ans += u;// 如果子树大小是偶数且当前节点恰好是中间两个节点之一,则累加另一个重心if (tot % 2 == 0 && sz[u] == tot / 2) ans += p[u];
}// 第一次DFS,计算子树大小、父节点和重儿子
void dfs1(int u, int fa) {int son = 0; // 重儿子初始化为0sz[u] = 1, p[u] = fa;for (int i = h[u]; ~i; i = ne[i]) {int j = e[i];if (j == fa) continue;dfs1(j, u);sz[u] += sz[j];// 更新重儿子if (sz[j] > sz[son]) son = j;}// 计算u的倍增数组calc_st(u, son);
}// 第二次DFS,换根DP计算每个子树的重心
void dfs2(int u, int fa) {int s1 = 0, s2 = 0, szu = sz[u]; // s1是重儿子,s2是次重儿子// 找出u的重儿子和次重儿子for (int i = h[u]; ~i; i = ne[i]) {int j = e[i];if (sz[j] >= sz[s1]) s2 = s1, s1 = j;else if (sz[j] > sz[s2]) s2 = j;}// 遍历所有儿子for (int i = h[u]; ~i; i = ne[i]) {int j = e[i];if (j == fa) continue;// 计算当前子树j的重心calc_ans(j);// 换根操作:将j作为新的根p[u] = j, sz[u] = n - sz[j];// 如果j不是重儿子,那么u的新重儿子是s1// 否则是次重儿子s2if (j != s1) calc_st(u, s1);else calc_st(u, s2);// 计算以u为根的子树的重心calc_ans(u);// 递归处理子树dfs2(j, u);}// 恢复现场p[u] = fa, sz[u] = szu;memcpy(f[u], g[u], sizeof f[u]);
}int main() {int T;scanf("%d", &T);while (T--) {// 初始化邻接表scanf("%d", &n);memset(h, -1, sizeof h), idx = 0;// 读入树结构for (int i = 0; i < n - 1; i++) {int a, b;scanf("%d%d", &a, &b);add(a, b), add(b, a);}// 第一次DFS预处理dfs1(1, -1);ans = 0;// 备份f数组,用于后续恢复memcpy(g, f, sizeof f);// 第二次DFS计算答案dfs2(1, -1);printf("%lld\n", ans);}return 0;
}

精简注释代码

#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>using namespace std;typedef long long LL;
const int N = 300010, K = 19, INF = 0x3f3f3f3f;int n;
vector<int> head[N];
int p[N], f[N][K], g[N][K], sz[N];
LL ans;void add(int u, int v) {head[u].push_back(v);
}// 倍增向儿子节点走
void calc_f(int u, int son) {f[u][0] = son;for (int i = 1; i < K; ++i) f[u][i] = f[f[u][i - 1]][i - 1];
}// 计算以u为根节点子树的重心
void calc_ans(int u) {int s = sz[u];for (int i = K - 1; i >= 0; --i) {if (f[u][i] && s - sz[f[u][i]] <= s / 2) u = f[u][i];}ans += u;if (s % 2 == 0 && sz[u] == s / 2) ans += p[u];
}void dfs1(int u, int fa) {int son = 0;sz[u] = 1, p[u] = fa;for (int v : head[u]) {if (v == fa) continue;dfs1(v, u);sz[u] += sz[v];if (sz[v] > sz[son]) son = v;}// 计算当前节点沿着重儿子走能到达的节点calc_f(u, son);
}// 换根
void dfs2(int u, int fa) {int s1 = 0, s2 = 0, u_sz = sz[u];for (int v : head[u]) {if (sz[v] > sz[s1]) {s2 = s1;s1 = v;}else if (sz[v] > sz[s2]) s2 = v;}for (int v : head[u]) {if (v == fa) continue;// 计算当前儿子的重心calc_ans(v);// 换根p[u] = v, sz[u] = n - sz[v];// 如果当前v是重儿子向第二大的儿子走, 否则直接走vv == s1 ? calc_f(u, s2) : calc_f(u, s1);calc_ans(u);dfs2(v, u);}// 恢复现场p[u] = fa, sz[u] = u_sz;// 因为换根后倍增更改了f[u]数组, 将f数组恢复memcpy(f[u], g[u], sizeof g[u]);
}int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);int T;cin >> T;while (T--) {cin >> n;for (int i = 0; i <= n; ++i) head[i].clear();for (int i = 0; i < n - 1; ++i) {int u, v;cin >> u >> v;add(u, v), add(v, u);}dfs1(1, -1);ans = 0;memcpy(g, f, sizeof f);dfs2(1, -1);cout << ans << "\n";}return 0;
}
http://www.dtcms.com/wzjs/210510.html

相关文章:

  • 电商数据中台镇江seo优化
  • 老网站用新域名东莞推广
  • 服务好的普通网站建设津seo快速排名
  • 长春网站建设营销q479185700刷屏百度退推广费是真的吗
  • php网站怎么做自适应百度网站制作
  • 2003 建设网站爱战网关键词挖掘查询工具
  • 天将建设集团有限公司网站百度推广竞价排名
  • 专注企业网站建设给公司做网站要多少钱
  • 整合wordpress济南搜索引擎优化网站
  • 网站架构变迁百度推广400电话
  • 宁波做公司网站的公司外包网络推广公司怎么选
  • 什么是营销型网站呢怎样做一个网站平台
  • 做公司网站软件网游百度搜索风云榜
  • 多个织梦dedecms网站怎么做站群百度官方官网
  • 河北做it的网站加快实施创新驱动发展战略
  • 怎么做网络推广品牌哪家强北京网站seo公司
  • 网站中flash怎么做今日头条新闻
  • php网站的客服窗口怎么做的查关键词
  • 河东苏州网站建设刷关键词排名seo软件
  • 网站设计与制作培训学校选择宁波seo优化公司
  • 模板式网站建设怎样建立个人网站
  • 深圳政府网站建设开平网站设计
  • 做JAVA基础编程题什么网站好长春百度推广排名优化
  • 有没有一起做网站的百度权重什么意思
  • 网站建设的公司业务seo公司多少钱
  • 金山石化网站建设seo最好的工具
  • 情公司做的网站怎么在网上销售
  • 网站建设H5 源码求个网站
  • 专业的购物网站定制成都百度推广公司电话
  • 廊坊做网站优化的公司百度快照优化