[BJOI2015]树的同构 && 树哈希教程

题目链接

有根树的哈希

离散数学中对树哈希的描述在这里。大家可以看看。

判断有根树是否同构,可以考虑将有根树编码。而编码过程中,要求保留树形态的特征,同时忽略子树顺序的不同。先来看一看这个方法:

不妨令一棵树的编码是个字符串\(T\)。

对于一个点\(u\),先求出\(u\)所有\(son_u\)的编码\(f_{son_u}\),然后将这些编码按字典序从小到大排序得到\(g_{1\cdots k}\)。那么\(f_u="0"+\sum\limits g_i+"1"\)。

令\(T\)为的编码为根节点的编码。

通过解码的方式可以验证这个算法的正确性。

这个只是数的编码,并不算是树的哈希。这个编码保证了正确性。而实际上,我们应用的时候,通常不会,也不允许这样做(时间空间都吃不消)。我们通常会采用数值的操作和取模的方法。而实际上,这个数值的操作要尽可能满足保留树形态的特征,同时忽略子树顺序的不同。加法、异或、排序等都是可以的。

树哈希的方法非常多,OIwiki上有三种常见的做法

无根树的哈希

一般的,通过选定根将无根树转成有根树,从而实现无根树的编码。而根一般选定为数的中心。如果有两个中心,就选定编码较小中心为根。对于哈希也是差不多的道理。

对于这道题,由于数据范围十分小,所以直接暴力编码,map判重即可。

#include <bits/stdc++.h>
using namespace std;

const int Maxn = 60;
struct edge {
    int To, Next;
    edge() {}
    edge( int _To, int _Next ) : To( _To ), Next( _Next ) {}
};
int Start[ Maxn ], Used;
edge Edge[ Maxn << 1 ];
inline void AddEdge( int x, int y ) {
    Edge[ ++Used ] = edge( y, Start[ x ] );
    Start[ x ] = Used;
    return;
}

map< string, int > Map;
string A[ Maxn ], B[ Maxn ], S;
int n, m, T[ Maxn ];
int Cnt, Rt[ Maxn ];

int Dfs1( int u, int Fa ) {
    int Size = 1, IsR = 1;
    for( int t = Start[ u ]; t; t = Edge[ t ].Next ) {
        int v = Edge[ t ].To;
        if( v == Fa ) continue;
        int T = Dfs1( v, u );
        if( T > n / 2 ) IsR = 0;
        Size += T;
    }
    if( n - Size > n / 2 ) IsR = 0;
    if( IsR ) Rt[ ++Cnt ] = u;
    return Size;
}

void Cal( int u, int Fa ) {
    for( int t = Start[ u ]; t; t = Edge[ t ].Next ) {
        int v = Edge[ t ].To;
        if( v == Fa ) continue;
        Cal( v, u );
    }
    A[ u ] = "0";
    int Cnt = 0;
    for( int t = Start[ u ]; t; t = Edge[ t ].Next ) {
        int v = Edge[ t ].To;
        if( v == Fa ) continue;
        B[ ++Cnt ] = A[ v ];
    }
    sort( B + 1, B + Cnt + 1 );
    for( int i = 1; i <= Cnt; ++i )
        A[ u ] = A[ u ] + B[ i ];
    A[ u ] = A[ u ] + "1";
    return;
}

int main() {
    scanf( "%d", &m );
    for( int i = 1; i <= m; ++i ) {
        memset( Start, 0, sizeof( Start ) );
        Used = 0;
        scanf( "%d", &n );
        for( int j = 1; j <= n; ++j ) {
            int x;
            scanf( "%d", &x );
            if( x == 0 ) continue;
            AddEdge( x, j );
            AddEdge( j, x );
        }
        Cnt = 0;
        Dfs1( 1, 0 );
        Cal( Rt[ 1 ], 0 );
        S = A[ Rt[ 1 ] ];
        for( int j = 2; j <= Cnt; ++j ) {
            Cal( Rt[ j ], 0 );
            if( A[ Rt[ j ] ] < S )
                S = A[ Rt[ j ] ];
        }
        if( Map.find( S ) == Map.end() ) Map[ S ] = i;
        printf( "%d\n", Map[ S ] );
    }
    return 0;
}

原文地址:https://www.cnblogs.com/chy-2003/p/11613061.html

时间: 2024-10-10 13:33:47

[BJOI2015]树的同构 && 树哈希教程的相关文章

[BZOJ4337][BJOI2015]树的同构(树的最小表示法)

4337: BJOI2015 树的同构 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1023  Solved: 436[Submit][Status][Discuss] Description 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相 同,那么这两个树是同

ZOJ 3602 Count the Trees 树的同构 (哈希)

链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4705 题意:给出两棵二叉树A和B,问分别处于A中的子树a和处于B中的子树b结构相同的有多少对. 思路:哈希的想法,不同的数字对应的是不同的结构,比如1代表着单独的叶子结点,2代表着有左子树是叶子结点而没有右子树的子树...每出现一种新的子树情形就记录下来,记录的方式是用dfs回溯过程中判断左子树和右子树组成的子树是否出现过(用pair记录子树的情况,也就是左右子树,两个

4337. [BJOI2015]树的同构【树哈希】

Description 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相 同,那么这两个树是同构的.也就是说,它们具有相同的形态. 现在,给你M个有根树,请你把它们按同构关系分成若干个等价类. Input 第一行,一个整数M. 接下来M行,每行包含若干个整数,表示一个树.第一个整数N表示点数.接下来N 个整数,依次

【树hs】[BJOI2015]树的同构

题目描述 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相 同,那么这两个树是同构的.也就是说,它们具有相同的形态. 现在,给你M个有根树,请你把它们按同构关系分成若干个等价类. 输入格式 第一行,一个整数M. 接下来M行,每行包含若干个整数,表示一个树.第一个整数N表示点数.接下来N 个整数,依次表示编号为1到N

[BJOI2015]树的同构

介于这道题没大佬发重心的写法,我就来凑个热闹 前置知识 一.树的重心 定义如下:删掉某节点\(i\)后,若剩余\(k\)个连通分量,那么定义\(d(i)\)为这些连通分量中节点数的最大值.所谓重心,就是使得\(d(i)\)最小的节点\(i\). 定理:重心最多有两个 证明:比较感性的理解:一个重心代表一种最优均分的方案,最坏的情况就是左右为难,两个划分同样优 求法: //size[x]为x子树大小 //maxl为最后min{d(i)} inline void DFS(re int x,re in

luogu P5043 【模板】树同构([BJOI2015]树的同构)

题面: 树是一种很常见的数据结构. 我们把N个点,N−1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相同,那么这两个树是同构的.也就是说,它们具有相同的形态. 现在,给你M个有根树,请你把它们按同构关系分成若干个等价类. 无根树Hash... 以树的每一个节点为根,求出Hash值后,存入一个数组里,排序. 比对就像这样 for(int j=1;j<=i;j++

ZOJ--3602--Count the Trees【DFS+Hash】树的同构

链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3602 题意:给出一棵有n个节点的二叉树和一棵有m个节点的二叉树,给出每个节点的左右子树信息,问这两棵树有几个相同的子树. 思路:树的同构,比赛时没想法,赛后看的别人的解题报告.实际上是给每个节点的左右子树一个哈希值,不用像字符串哈希那么麻烦,直接给每个子树一个数字标记就行了,用map映射每个节点的左子树和右子树信息对应一个标记值,用DFS给两棵树的每个节点都赋一个哈

BZOJ 3899 仙人掌树的同构 仙人掌同构+KMP算法

题目大意:给定一棵仙人掌,求有多少自同构 仙人掌同构问题= = 曾经出过一个判断两个仙人掌是否同构的题,感觉和这个题很类似 首先假设这是一棵树,考虑怎么做 我们首先找到树的重心(如果有两个就在中间加一个点变成一个) 然后把树Hash 对于一棵树 如果某一哈希值的子树有k个 就把答案乘上一个k! 现在变成了仙人掌,那么我把每个环变成一个红点连向环上的所有点,然后把原先环上的边拆除,可以得到一棵树,按树同构做就行了 为了区分红点和普通点的区别,需要为红点设置不同的哈希参数 但是这样有一个BUG,就是

bzoj4337树的同构

树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相 同,那么这两个树是同构的.也就是说,它们具有相同的形态. 现在,给你M个有根树,请你把它们按同构关系分成若干个等价类. 树哈希模板题. Code #include<iostream> #include<cstdio> #include<algor