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

2/9 树的重心树的同根 总结

树的重心

定义:以这个点为根时,树各节点深度之和最小的点叫树的重心。

以这个点为根时,最大的子树最小的点叫树的重心。

以这个点为根时,所有子树大小不超过 \(n/2\) 的点叫树的重心。

为什么这仨是一回事?

我们拿第一个定义的换根dp方程来说明这三个定义等价。

\[dp[v]=dp[u]+n-2\times siz[v] \]

注意到,如果存在一个 \(v\),使得 \(2\times siz[v]>n\),那么如果往这里遍历,\(dp[v]\)会变小。否则 \(dp[v]\) 就会变大。

注意到,这样的 \(v\) 有且仅有1个

这说明在这个点,所有点与它的距离最小。并且所有子树的大小都不超过 \(n/2\) 。 根据 \(siz[v]\times 2\le n\) 得到。

还可以进一步分析得到问题二。

如果这个点上下移动了,只有两种情况:

  • 子树中有 size 等于 n/2 的子树

  • 子树中所有子树 size 都小于 n/2

不论以上哪种情况,往任意子树方向走,都会让最大的子树的大小变大(最少都是不变)。

比较显然就不画图了。

树的同构

对于结构相同,节点编号可能不同的两棵树,我们称它们是同构的。

例子:

image

我们认为这两棵树同构。(上图是无根树)

有根树的同构判断

括号序列表示树

很简单,举个例子就能看懂。

image

对于每个叶子结点,我们用 () 表示。对于每个节点,我们依次写出它们的子节点的子树的表示,然后用一个括号括起来。

如上图的 \(u\) 节点,我们用 ( () (()()()) (()()) ) 表示以 \(u\) 为根的子树(实际实现中不加空格,这里为了方便阅读就加了空格)。

上图整颗树的括号序列为 ((()(()()())((())))(()()))

求出有根树的括号序列,判断是否相同,就可以知道两棵树的结构是否相同啦!

注意 \(u\) 的各个子树的括号序列要排序,因为可能访问顺序不同。

时间复杂度:\(O(n^2\log n)\),每访问一个节点排一次序。

点击展开代码
//根为1
#include<bits/stdc++.h>
using namespace std;
struct tree{int n;string ans[100005];vector<int> g[100005];void init(){cin>>n;for(int i=1,x,y;i<n;++i){cin>>x>>y;g[x].push_back(y);g[y].push_back(x);}}void dfs(int u,int Fa){vector<string> tmp;for(auto v:g[u]){if(v==Fa) continue; dfs(v,u);tmp.push_back(ans[v]);} sort(tmp.begin(),tmp.end());ans[u]="(";for(auto v:tmp) ans[u]+=v;ans[u]+=")";}
}t1,t2;
int main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);t1.init();t2.init();t1.dfs(1,0);t2.dfs(1,0);if(t1.ans[1]==t2.ans[1]) cout<<"Yes";else cout<<"No";cout<<"\n"<<t1.ans[1]<<"\n"<<t2.ans[1];return 0;
}
哈希判断同构

我们设单个节点的哈希值为常数 1。

如果已有 \(u\) 子树 \(v\) 的 哈希值 \(ans[v]\),如何得到 \(u\) 的哈希值?

可以采用加和。

\[ans[u]=(1+\sum_{v\in son(u) ans[v]})\%mod \]

实际实现中,累加子树哈希值之前,对子树哈希的值再进行一次哈希。

一个好用的二进制打乱函数以及不容易被卡的随机函数。

using ull=unsigned long long;
const ull mod=mt19937_64(time(0))();
ull f(ull t){t^=mod;t^=t<<13;t^=t>>7;t^=t<<17;t^=mod;return t;
}

最终哈希公式:

\[ans[u]=(1+\sum_{v\in son(u) f(ans[v])})\%mod \]

时间复杂度:\(O(n)\)

点击展开代码
//默认根为1
#include<bits/stdc++.h>
using namespace std;
using ull=unsigned long long;
struct tree{int n; ull mod=mt19937_64(time(0))();ull ans[1000005];vector<int> g[1000005];void init(){cin>>n;for(int i=1,x,y;i<n;++i){cin>>x>>y;g[x].push_back(y);g[y].push_back(x);}}ull shufule(ull t){t^=mod;t^=t<<13;t^=t>>7;t^=t<<17;t^=mod;return t;}void dfs(int u,int Fa){ans[u]=1;for(auto v:g[u]){if(v==Fa) continue; dfs(v,u);ans[u]+=shufule(ans[v]);}}
}t1,t2;
signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);t1.init();t2.init();t1.dfs(1,0);t2.dfs(1,0);if(t1.ans[1]==t2.ans[1]) cout<<"YES";else cout<<"NO"; return 0;
}

无根树判断同构

考虑转化为有根树判断同构。

如何选根节点?肯定要选一个特殊且在树上唯一的节点。

想到了树的重心,虽然可能有两个,但是可以把两个重心都当做根判断一遍。

例题:SP7826 TREEISO - Tree Isomorphism

就是让你判断两棵无根树是否同构。

点击展开代码
#include<bits/stdc++.h>
using namespace std;
using ull=unsigned long long;
int n;
vector<int> g[500005];
ull siz[500005],maxv[500005];
mt19937_64 rng(time(0));
ull mod=rng();
ull shufule(ull t){t^=mod;t^=t<<13;t^=t>>7;t^=t<<17;t^=mod;return t;
}
void dfs1(int u,int Fa,vector<ull> &w){siz[u]=1;maxv[u]=0;for(int v:g[u]){if(v==Fa) continue;dfs1(v,u,w);siz[u]+=siz[v];maxv[u]=max(maxv[u],siz[v]);}maxv[u]=max(maxv[u],n-siz[u]);if(maxv[u]<=n/2) w.push_back(u);
}
ull get_hash(int u,int Fa){ull res=1;for(auto v:g[u]){if(v==Fa) continue;res+=shufule(get_hash(v,u));}return res;
}
pair<ull,ull> solve(){if(n==1) return {0,1};for(int i=1,x,y;i<n;i++){cin>>x>>y;g[x].push_back(y);g[y].push_back(x);}vector<ull> w;dfs1(1,0,w);ull h1=get_hash(w[0],0);if(w.size()==h1) return {0,h1};ull h2=get_hash(w[1],0);return {min(h1,h2),max(h1,h2)};
}
signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);int T;cin>>T;while(T--){fill(siz+1,siz+1+n,0);fill(maxv+1,maxv+1+n,0);cin>>n;for(int i=1;i<=n;i++) g[i].clear();auto h1=solve();for(int i=1;i<=n;i++) g[i].clear();auto h2=solve();cout<<(h1==h2?"YES":"NO")<<"\n";}return 0;
}
http://www.jsqmd.com/news/362643/

相关文章:

  • Linux 中断驱动程序--按键中断驱动
  • 【开题答辩全过程】以 基于SpringBoot云旅行微信小程序的设计和实现为例,包含答辩的问题和答案
  • 2026年高口碑系统门窗五金和五金配件产品推荐排行榜,提升家装体验 - 睿易优选
  • 2026年智能工厂规划全流程解析:浙江及江苏优秀企业推荐 - 孟哥商业圈
  • AI应用架构师分享:智能产品推荐AI系统的模型压缩技术
  • 2026年公司注册厂家权威推荐榜:南非公司注册、境外投资备案ODI公司、广州境外投资备案ODI、德国公司注册选择指南 - 优质品牌商家
  • AI 时代的前端技术:从系统编程到 JavaScript/TypeScript
  • 【claude】Claude 如何节省 Token?10 个实用技巧让成本降低 70%
  • 2026年评价高的不锈钢烟囱塔公司推荐:碳钢烟囱塔、角钢监控塔、道路监控塔、钢管监控塔、镀锌烟囱塔架选择指南 - 优质品牌商家
  • 2026年实测对比:宁波智能工厂规划服务商TOP5深度解析 - 孟哥商业圈
  • 【开题答辩全过程】以 河市富达购物微信小程序为例,包含答辩的问题和答案
  • 2026四川加气砖优选指南:从蒸压砌块到轻质隔墙的五家可靠之选 - 深度智识库
  • 递归思想的思路分享
  • Flutter for OpenHarmony 习惯养成 App:用打卡机制打造自律生活的可视化引擎
  • 【claude】最新Claude Opus 4.6 深度评测:AI编程工具的新王者诞生!
  • 深入解析:小白也能看懂的DeepSeek-R1本地部署指南
  • Flutter for OpenHarmony音乐播放器实战:打造动态波形可视化与沉浸式播放体验
  • 2026年自动洗瓶机厂家推荐:饮料瓶洗瓶机/啤酒瓶洗瓶机/回收瓶洗瓶机/实验室洗瓶机/毛刷式洗瓶机/选择指南 - 优质品牌商家
  • Flutter for OpenHarmony BMI 健康计算器:打造支持深色模式的智能健康工具
  • 【开题答辩全过程】以 基于Springboot图书管理系统为例,包含答辩的问题和答案
  • 2026年碰碰车厂家推荐:逍遥乐吧车/360摇滚乐吧车/亲子双人碰碰车/公园碰碰车/发光漂移碰碰车/商场碰碰车/选择指南 - 优质品牌商家
  • 2026年实测TOP3智能工厂规划服务商深度对比 - 孟哥商业圈
  • P3195 [HNOI2008] 玩具装箱
  • 题解:AWC 0001
  • 2026牛客寒假算法基础集训营4 题解
  • 2026年评价高的三柱避雷塔公司推荐:监控铁塔、角钢监控塔、角钢避雷塔、道路监控塔、钢管避雷塔、镀锌监控塔架选择指南 - 优质品牌商家
  • AI不是在杀死SaaS,而是在逼传统软件回到它真正值钱的那一层
  • YouTube 文字转语音怎么用?AI 配音提升效率与内容产出的完整指南
  • 2026年江西新工厂规划避坑指南:五大服务商深度评测;江西五大公司排名与常见误区解析 - 孟哥商业圈
  • 只知道WinPE?这款两款Linux PE维护系统,轻松化解Linux运维难题