树的同构模板题(法1.最小表示法+法2.树哈希)

树的同构

  • problem
  • solution
  • code
  • solution
  • code

problem

模板题

solution

Ⅰ. 最小表示法

将树转化为 0/10/10/1 括号序列:从根开始 dfs\text{dfs}dfs000 就往下遍历一个儿子,111 就返回,构成一个 2×n2\times n2×n 的括号序列。

显然,括号序列与树的形态是唯一对应的。

  • 有根树
    • 若儿子遍历顺序是固定的。显然括号序列只有唯一一种。
    • 若儿子遍历顺序不固定。就会有多种合法的括号序列,不妨钦定字典序最小的为这棵树的括号序列,这个特殊的括号序列有单独的名称:最小表示

显然,两棵有根树的最小表示相同是同构的充要条件

具体实现:递归地构造,对于点 uuu,先把儿子 vvv 的括号序列都处理出来后,按照儿子的字典序从小到大排序,然后顺次接起来。在这个拼接的括号序列外面用 000(进入 uuu 子树求解)111(完成 uuu 子树内的遍历) “包起来”就表示把 uuu 子树遍历完然后返回上一层的最小表示了。

时间复杂度为 O(n2)O(n^2)O(n2)。最坏情况为链。

因为括号序列肯定是用字符串 string\text{string}string 类型储存。

那么 f[u]+=f[v]f[u]+=f[v]f[u]+=f[v] 这一句话的操作复杂度其实是 O(len)O(len)O(len) 的。

  • 无根树

比较暴力的想法就是对于每个点都当作根,做一遍 dfs\text{dfs}dfs。时间复杂度为 O(n3)O(n^3)O(n3)

对于本题而言,又有 mmm 棵树,时间复杂度就是 O(n3m)O(n^3m)O(n3m) 的。

实际上,最小表示法就是为了定义一个规则,让一棵树拥有唯一的括号序列。如果喜欢最大表示法也是可以的。

所以可以对无根树找一个特殊的规则,来区别不同构的树。

这里我们选择重心为根时的括号序列当作无根树的括号序列代表。

两个重心的话就取字典序较小的。

时间复杂度就降为 O(n2m)O(n^2m)O(n2m)

code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 55
int n, m, Max, cnt;
string Min;
struct node { int to, nxt; }E[maxn << 1];
int head[maxn], siz[maxn], MaxSiz[maxn];
string f[maxn], g[maxn], mp[maxn];void init() {memset( head, -1, sizeof( head ) );memset( MaxSiz, 0, sizeof( MaxSiz ) );cnt = 0; Max = n; Min = "1";
}void addedge( int u, int v ) {E[cnt] = { v, head[u] };head[u] = cnt ++;
}void dfs1( int u, int fa ) {siz[u] = 1;for( int i = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( v == fa ) continue;dfs1( v, u );siz[u] += siz[v];MaxSiz[u] = max( MaxSiz[u], siz[v] );}MaxSiz[u] = max( MaxSiz[u], n - siz[u] );Max = min( Max, MaxSiz[u] );
}void dfs2( int u, int fa ) {f[u] = "0";for( int i = head[u];~ i;i = E[i].nxt )if( E[i].to ^ fa ) dfs2( E[i].to, u );int tot = 0;for( int i = head[u];~ i;i = E[i].nxt )if( E[i].to ^ fa ) g[++ tot] = f[E[i].to];sort( g + 1, g + tot + 1 );for( int i = 1;i <= tot;i ++ ) f[u] += g[i];f[u] += "1";
}int main() {scanf( "%d", &m );for( int j = 1;j <= m;j ++ ) {scanf( "%d", &n );init();for( int i = 1, x;i <= n;i ++ ) {scanf( "%d", &x );if( x ) addedge( x, i ), addedge( i, x );}dfs1( 1, 0 );for( int i = 1;i <= n;i ++ )if( MaxSiz[i] == Max ) {dfs2( i, 0 );Min = min( Min, f[i] );}mp[j] = Min;for( int i = 1;i <= j;i ++ )if( mp[i] == mp[j] ) {printf( "%d\n", i );break;}}return 0;
}

solution

Ⅱ . 树哈希

多项式哈希,即 [1,r][1,r][1,r] 的哈希值减去 r−l+1r-l+1rl+1 乘上 [1,l][1,l][1,l] 的哈希值。

同理,有根树且儿子有顺序,哈希就是一一对应,正确的。

如果有根树且儿子没顺序,就按照儿子的哈希值排序后,再哈希。

有根树扩展到无根树也是寻找重心。

得出哈希值后暴力比较即可。

时间复杂度为 O(nmlog⁡n)O(nm\log n)O(nmlogn),瓶颈在于排序。(如果你不喜欢 log⁡\loglog,基排就行)

其实树哈希就是没有最小表示法的字符串相关操作带来的巨大复杂度而已。

哈希唯一的缺点就是会冲突,不能保证一定稳定,但也不至于很容易就被卡掉。

code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
#define mod 1019260817
#define base 19491001
#define maxn 55
struct node { int to, nxt; }E[maxn << 1];
pair < int, int > f[maxn], g[maxn];
int Pow[maxn], Hash[maxn], head[maxn], siz[maxn], MaxSiz[maxn], dep[maxn];
int n, m, cnt, rt1, rt2, Max;void addedge( int u, int v ) {E[cnt] = { v, head[u] };head[u] = cnt ++;
}void dfs1( int u, int fa ) {siz[u] = 1;for( int i = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( v == fa ) continue;dfs1( v, u );siz[u] += siz[v];MaxSiz[u] = max( MaxSiz[u], siz[v] );}MaxSiz[u] = max( MaxSiz[u], n - siz[u] );if( MaxSiz[u] < Max ) Max = MaxSiz[u], rt1 = u, rt2 = 0;else if( MaxSiz[u] == Max ) rt2 = u;
}void dfs2( int u, int fa ) {Hash[u] = Pow[siz[u] = 1] * dep[u] % mod;for( int i = head[u];~ i;i = E[i].nxt ) if( E[i].to ^ fa ) dep[E[i].to] = dep[u] + 1, dfs2( E[i].to, u );int tot = 0;for( int i = head[u];~ i;i = E[i].nxt )if( E[i].to ^ fa ) g[++ tot] = { Hash[E[i].to], siz[E[i].to] };sort( g + 1, g + tot + 1 );for( int i = 1;i <= tot;i ++ )Hash[u] = ( Hash[u] + g[i].first * Pow[siz[u]] ) % mod, siz[u] += g[i].second;
}signed main() {Pow[0] = 1;for( int i = 1;i < maxn;i ++ ) Pow[i] = Pow[i - 1] * base % mod;scanf( "%lld", &m );for( int k = 1;k <= m;k ++ ) {memset( MaxSiz, 0, sizeof( MaxSiz ) );memset( head, -1, sizeof( head ) );cnt = 0; Max = mod;scanf( "%lld", &n );for( int i = 1, x;i <= n;i ++ ) {scanf( "%lld", &x );if( x ) addedge( i, x ), addedge( x, i );}dfs1( 1, 0 );dep[rt1] = 1;dfs2( rt1, 0 );f[k].first = Hash[rt1];if( ! rt2 ) goto pass;dep[rt2] = 1;dfs2( rt2, 0 );f[k].second = Hash[rt2];if( f[k].first > f[k].second ) swap( f[k].first, f[k].second );pass : ; }for( int i = 1;i <= m;i ++ )for( int j = 1;j <= i;j ++ )if( f[i] == f[j] ) { printf( "%lld\n", j ); break; }return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/316859.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CF917B MADMAX

CF917B MADMAX 题意&#xff1a; Alice和Bob有一个n个点m条边的DAG&#xff0c;每条边上有一个小写英文字母表示权值&#xff0c;Alice和Bob每人有一个棋子&#xff0c;每个人放在一个节点上&#xff08;可以放在同一个节点上&#xff09;。 第一轮Alice可以沿一条边把棋子移…

CF1534F:Falling Sand(tarjan、贪心、dp)

解析 本题做的过程大概是&#xff1a;到了正解门口决定回头&#xff0c;绕了地球一圈最后从另一边走到正解了… 我的分析&#xff1a; 首先 easy version 很简单&#xff0c;如果从一列最大值可以走到另一列最大值&#xff0c;那么后者就没用了。缩完点所有的零度点就是答案。…

Ocelot 入门Demo系列(01-Ocelot极简单Demo及负载均衡的配置)

来源&#xff1a;https://www.cnblogs.com/7tiny/p/10493805.html【前言】Ocelot是一个用.NET Core实现并且开源的API网关&#xff0c;它功能强大&#xff0c;包括了&#xff1a;路由、请求聚合、服务发现、认证、鉴权、限流熔断、并内置了负载均衡器与Service Fabric、Butterf…

CF464E The Classic Problem(主席树+哈希+最短路)

CF464E The Classic Problemproblemsolutioncodeproblem 题目链接 solution 经典题。 本题很特别的是每条边的边权都是 222 的幂&#xff0c;而且数据读入的是指数。 所以可以将路径权值看作一个二进制串&#xff0c;每加一条边就是对应二进制位 111&#xff0c;当然会有相…

CF1192B Dynamic Diameter(LCT)

Foreword\text{Foreword}Foreword zhengrunzhe 大佬的矩阵做法过于神奇&#xff0c;蒟蒻无法理解… 欧拉序的做法确实非常巧妙&#xff0c;但我也想不到这么神仙的做法… 提供一种可能简单一些的 LCT 做法。 由于本人 LCT 无法像大佬那么神&#xff0c;讲的会比较详细一些&…

博弈论(基础概念+例题)

博弈论(b站视频) 文章目录一些概念以Nim游戏为例Nim游戏介绍定义 必败/必胜局面必败/必胜局面的判定引理Nim游戏判定引理的等价命题有向图游戏对判定引理的数学描述-Sg函数有向图游戏的和题目&#xff1a;[有向图游戏][有向图游戏的和][构造/转化类]一些概念 以Nim游戏为例 Ni…

.NET Core 3.0 linux 部署小贴士

dotnet core 3.0 目前还是测试版&#xff0c;在linux下安装 sdk 需要有一些注意事项1.下载urlhttps://dotnet.microsoft.com/download/thank-you/dotnet-sdk-3.0.100-preview-009812-linux-x64-binaries2.安装指令mkdir -p $HOME/dotnet && tar zxf dotnet-sdk-3.0.100…

莓良心(第二类斯特林数)

莓良心problemsolutioncodeproblem 莓在执行任务时&#xff0c;收集到了 nnn 份岩浆能源&#xff0c;其中第 iii 份的能量值是 wiw_iwi​ &#xff0c;她 决定将它们分成恰好 kkk 组带回基地&#xff0c;每一组都要有至少 111 份能源。 每一组能源会对运输设备产生负荷值&…

CF1654E Arithmetic Operations(根号分治)

解析 降智谔谔题。 明明都把做法大块想出来&#xff0c;最后很沙雕的一步掉牛角尖里了… 无能狂怒之下写了一发实际复杂度 n2n^2n2 的代码。 不过暴力艹题还是很爽的。 可能确实不太好卡吧&#xff0c;让我自己构造我是卡不掉。 更何况CF应该不会有人和我一样在如此沙雕的地方…

P2575 高手过招

P2575 高手过招 题意&#xff1a; AKN玩游戏玩累了&#xff0c;于是他开始和同伴下棋了&#xff0c;玩的是跳棋&#xff01;对手是wwx&#xff01;这两位上古神遇在一起下棋&#xff0c;使得棋局变得玄幻莫测&#xff0c;高手过招&#xff0c;必有一赢&#xff0c;他们都将用…

PuppeteerSharp: 更友好的 Headless Chrome C# API

前端就有了对 headless 浏览器的需求&#xff0c;最多的应用场景有两个UI 自动化测试&#xff1a;摆脱手工浏览点击页面确认功能模式爬虫&#xff1a;解决页面内容异步加载等问题也就有了很多杰出的实现&#xff0c;前端经常使用的莫过于 PhantomJS 和 selenium-webdriver&…

尽梨了(贪心+dp)

尽梨了problemsolutioncodeproblem 丰之崎学园附近共有 nnn 个商店&#xff0c;在时刻 000 时&#xff0c;英梨梨从学园出发开始购物。 从学园走到任意一个商店&#xff0c;或从一个商店走到另一个商店需要 111 单位时间。 如果英梨梨在时刻 ttt 到达了商店 iii &#xff0c…

CF1612G Max Sum Array

解析 被蓝题虐了。&#xff08;悲 确实不太难&#xff0c;就是没往那边想。 考虑如果某个值的下标分别位 i1,i2,...,ini_1,i_2,...,i_ni1​,i2​,...,in​ 那么如何计算贡献。 每一个下标和前面统计时作为被减数&#xff0c;和后面统计时作为减数&#xff0c;所以 iki_kik​ …

【Acwing 219. 剪纸游戏】

【Acwing 219. 剪纸游戏】 题意&#xff1a; 给定一张 NM 的矩形网格纸&#xff0c;两名玩家轮流行动。 在每一次行动中&#xff0c;可以任选一张矩形网格纸&#xff0c;沿着某一行或某一列的格线&#xff0c;把它剪成两部分。 首先剪出 11 的格纸的玩家获胜。 两名玩家都采取…

中国.NET:东莞+长沙.NET俱乐部现场花絮及合肥、苏州、上海等地活动预

《传承有序》与微软技术的发展历程相似&#xff0c;微软俱乐部的发展经历着沉沉浮浮&#xff0c;曾经随着微软走向封闭与固执&#xff0c;.NET社区年轻一代的声音被忽略&#xff0c;.NET社区后继无人。社区的沉默是可怕的&#xff0c;很多社区沉寂消亡。但是在2018年&#xff0…

团不过(dp)

团不过problemsolutioncodeproblem 由比滨结衣和雪之下雪乃在玩 NimNimNim 游戏。 共有 nnn 堆石子&#xff0c;双方轮流操作&#xff0c;每次可以从一堆非空石子堆中取走任意个石 子&#xff0c;取走最后一个石子的人胜利。 她们决定让由比滨结衣先手&#xff0c;但忘记了每…

【AcWing 235. 魔法珠

【AcWing 235. 魔法珠 题意&#xff1a; 有n堆魔法珠&#xff0c;第i堆有ai个&#xff0c;两个人轮流进行以下操作&#xff1a; 当轮到某人操作时&#xff0c;如果每堆中魔法珠的数量均为 1&#xff0c;那么他就输了。 问谁赢谁输 题解&#xff1a; 经典博弈论问题 注意本…

P5039 [SHOI2010]最小生成树(网络流)

解析 我大受震撼。 真的没想到是网络流。&#xff08;事后诸葛&#xff1a;看几百的数据范围应该试试枚举算法&#xff09; 一道比较开眼界的题吧。 然而用一个十分钟写完的假做法可以骗90分&#xff0c;还是挺香的 首先是一个没什么意思的转化&#xff1a;操作等价于给一条边…

Abp vNext 切换MySql数据库

Abp vNext是Abp的下一代版本&#xff0c;目前还在经一步完善&#xff0c;代码已经全部重写了&#xff0c;好的东西保留了下来&#xff0c;去除了很多笨重的东西&#xff0c;从官宣来看&#xff0c;Abp vNext主要是为了以后微服务架构而诞生的。从源码来看&#xff0c;Abp vNext…

P1199 [NOIP2010 普及组] 三国游戏

P1199 [NOIP2010 普及组] 三国游戏 题意&#xff1a; 有n个武将&#xff0c;每两个武将之间都有默契值&#xff0c;你和电脑玩游戏&#xff0c;轮流选一个。轮到计算机挑选时&#xff0c;它会尝试将对手军队中的每个武将与当前每个自由武将进行一一配对&#xff0c;找出所有配…