JSOI2016 独特的树叶 树的Hash判同构

hash - 同构 P5043 【模板】同构([BJOI2015]同构) fx=1+∑y∈sonxfy×prime(sizey)f_x = 1 + \sum_{y\in son_x}{f_y \times prime(size_y)}fx​=1+y∈sonx​∑​fy​×prime(sizey​) gx=gfa−fx∗prime(sizex)+fxg_x = g_{fa} - f_x * prime(size_x) + f_xgx​=gfa​−fx​∗prime(sizex​)+fx​ 其中: fxf_xfx​ 表示 阅读详情

题目大意

给你两棵树,一棵有N个节点的树A,另一棵有N+1个节点的树B,已知B树可以删去一个结点后变成A树。问删去的是哪个节点,如有多个合法的节点则输出在B树中编号最小的一个。

N105

解题思路

如果知道怎么判树的同构,那么这题就可以顺利解决,那么就先考虑一下怎么判树的同构。

树的同构:一个很显然的思路就是把每个节点当作根做一次树的Hash,判断两棵树是否同构时,只需把一颗树的所有Hash值丢到map中再对于另一棵树的每个Hash值看看map里有没有就可以了。

那怎么求树的Hash

对于一棵子树我们可以把直接相连的儿子的Hash值排个序,再类似字符串Hash的方法合并成一个状态值,最后把这个状态值Hash一下就可以求出当前子树的Hash值,可是如果暴力把一棵树所有的Hash值求出来是O(N2logN),因为要以每个点为根跑一遍。而只需要动态移根,每次更改状态就可以O(NlogN)求出一棵树的所有同构的Hash值。

回到本题,我们可以先求出与A树同构的所树的Hash值,对于B树我们能删的节点只能是入度为1的点,那么同样我们求出与树B同构的所有树,那么对于每个入度为1的点,我们求出删掉当前点是新的B树的Hash值,再看看A树的同构是否有相等即可。

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <map>

using namespace std;
typedef unsigned long long LL;

const int MAXN = 1e5 + 5, MAXT = 1e7 + 5;
const LL Pri = 99877171;

struct HashList {LL Num; int New;} Map[MAXT];
struct Info {
    int Num, Poi;
    Info (void) {};
    Info (int A, int B) {Num = A, Poi = B;};
};

vector<Info> Son[MAXN];
vector<LL> Suf[MAXN];

LL State[MAXN], Pow[MAXN], Hash[MAXN];
int Cnt, N, Real[MAXN], Siz[MAXN], tot, Next[MAXN * 2], Go[MAXN * 2], Last[MAXN];
bool Exist[MAXT];

int Get(LL Sta) {
    int Now = Sta % MAXT;
    while (Map[Now].New != 0 && Map[Now].Num != Sta) Now = (Now + 1) % MAXT;
    if (!Map[Now].New) Map[Now].New = ++ Cnt, Map[Now].Num = Sta;
    return Map[Now].New;
}

void Link(int u, int v) {
    Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v;
}

bool Cmp(Info A, Info B) {return A.Num < B.Num;}

void Basis(int Now, int Pre) {
    for (int p = Last[Now]; p; p = Next[p]) {
        int v = Go[p];
        if (v == Pre) continue;
        Basis(v, Now);
        Son[Now].push_back(Info(Hash[v], v));
    }
    sort(Son[Now].begin(), Son[Now].end(), Cmp);
    int Size = Son[Now].size();
    LL Sta = 0;
    for (int i = 0; i < Size; i ++) 
        Sta = Sta * Pri + Son[Now][i].Num;
    Hash[Now] = Get(Sta);
}

void All(int Now, int Pre, LL PreS) {
    LL Sta = 0;
    if (Now != 1) Son[Now].push_back(Info(PreS, Pre));
    int Size = Son[Now].size();
    sort(Son[Now].begin(), Son[Now].end(), Cmp);
    for (int i = 0; i < Size; i ++) 
        Sta = Sta * Pri + Son[Now][i].Num;
    State[Now] = Sta, Siz[Now] = Size;
    Real[Now] = Get(Sta);
    Suf[Now].clear();
    Suf[Now].resize(Size + 1);
    for (int i = Size - 1, j = 0; i + 1; i --, j ++) 
        Suf[Now][i] = Suf[Now][i + 1] + Pow[j] * Son[Now][i].Num;
    LL Pref = 0;
    for (int i = 0; i < Size; i ++) {
        Info v = Son[Now][i];
        if (i) Pref = Pref * Pri + Son[Now][i - 1].Num;
        if (v.Poi == Pre) continue;
        int Num = Suf[Now][i + 1];
        All(v.Poi, Now, Get(Pref * Pow[Size - i - 1] + Suf[Now][i + 1]));
    }
}

void Solve(int Ord) {
    Basis(1, 0);
    All(1, 0, 0);
    if (Ord == 1) {
        for (int i = 1; i <= N; i ++) Exist[Real[i]] = 1;
        return;
    }
    if (!Next[Last[1]]) {
        int Now = Go[Last[1]];
        if (Exist[Get(State[Now] - Pow[Siz[Now] - 1])]) {
            printf("1\n");
            return;
        }
    }
    for (int i = 2; i <= N + 1; i ++) {
        if (Next[Last[i]]) continue;
        int Now = Go[Last[i]];
        if (!Exist[Get(State[Now] - Pow[Siz[Now] - 1])]) continue;
        printf("%d\n", i);
        return;
    }
}

void Prepare() {
    Pow[0] = 1;
    for (int i = 1; i <= N + 1; i ++) Pow[i] = Pow[i - 1] * Pri;
}

void Clear() {
    tot = 0;
    memset(Last, 0, sizeof Last);
    memset(Son, 0, sizeof Son);
    memset(Hash, 0, sizeof Hash);
}

int main() {
    scanf("%d", &N);
    Prepare();
    for (int i = 1; i < N; i ++) {
        int u, v;
        scanf("%d%d", &u, &v);
        Link(u, v), Link(v, u);
    }
    Solve(1);
    Clear();
    for (int i = 1; i <= N; i ++) {
        int u, v;
        scanf("%d%d", &u, &v);
        Link(u, v), Link(v, u);
    }
    Solve(2);
}
rk3326 MIPI休眠唤醒黑屏有背光问题 ** 1.问题现象: ** **【开机起来】**开机起来可以正常显示 **【按power按键休眠】**第二次按下Power按键进入休眠,隔10s,再按Power按键唤醒。 **【出现结果】**屏幕黑屏有背光,并提示drm time out错 **【lcd信息】**lcd为480*800 2lane的mipi **【复现概率】**第二次按下休眠唤醒必然复现 【具体错误log如下】 //第一次休眠的log// [ 54.858511] dw-mipi-dsi ff450000.dsi: final DSI- 阅读详情

相关推荐

保姆级教程:用PyTorch从零实现SDE扩散模型(附完整代码与MNIST实战)

本文提供了一份详细的PyTorch教程,指导读者从零开始实现SDE扩散模型,并附有完整的代码示例和MNIST数据集实战。通过解释随机微分方程(SDE)的核心概念、模型架构设计、训练流程实现以及采样生成过程,帮助开发者掌握扩散模型的构建与应用。

weixin_28677005的博客 235

哈希(Tree Hash

哈希(Tree Hash) 顾名思义,对进行哈希,经常断两个是否同构。一下均为对有根的算法,而无根只需要找重心 无权 方法一 fnow=sizenow×∑fson(now,i)×seedi−1 f_{now}=size_{now} \times \sum f_{son(now,i)}\times seed^{i-1} fnow​=sizenow​×∑fson(now,i)​×seedi−1 其中 fxf_xfx​ 为以节点 xxx 为根的子对应的哈希值。特殊地,我们令叶子节点的哈希值为 11

jiahonghao2002的博客 5330

【有限元分析】风力发电机塔筒筒体校核——matlab雨流计数法.rar

【有限元分析】风力发电机塔筒筒体校核——matlab雨流计数法.rar

同构

大佬的文章 #include<cstdio> #define MaxTree 10 #define Null -1 #include<stdio.h> #define MaxTree 10 #define Null -1 //将Null定义为-1而不能是0,因为数组下标为0的地方仍保存有节点 typedef char ElementType; typedef int Tree; struct TreeNode{ //定义二叉节点 ElementType Dat

yyj的博客之旅 2277

同构/哈希

题目 P5043 【模板】同构([BJOI2015]同构同构与哈希 断两棵是否同构,可以用哈希实现,两颗的哈希值一样时,我们可以定他们同构的哈希只在有根中有意义。当要定两棵是否同构时,我们可以选择它们的重心为根进行哈希。时间复杂度为O(nlogn)O(nlogn)O(nlogn)。 关于哈希,用的比较多的方法是将子从小到大排序,对于第iii个子,乘上第iii个质数。 val[y]=∑x∈sonyval[x]∗prime[size(x)]val[y]=\sum_{x\i

陈颜的博客 816

断两棵二叉是否同构,三种实现方式(递归、队列、堆栈)

例1:下图两棵同构,因为对T2,交换A左右孩子;交换B左右孩子,交换G左右孩子,经过3次交换后,T2=T1,即两棵全等,所以说这两棵同构的。例2:下面两棵异构,因为对T2-B的左右孩子不等于T1-B,所以异构。 数组表示法:一棵结点数为5的二叉可以表示为下图 缺点:浪费空间,5个结点就需要13个空间来保存,增删改不方便。 优点:空间地址连续,能够快速查找 链表表示法:一棵结点数为5的二叉可以表示为下图 优点

不完美的红烧肉的博客 5923

Hash

我们有时需要断一些是否同构。这时,选择恰当的Hash方式来将映射成一个便于储存的Hash值(一般是 32 位或 64 位整数)是一个优秀的方案。 Hash定义在有根上。断无根同构的时候,可以比较重心为根的Hash值或者比较每个点为根的Hash值。 哈希有很多种哈希方式,下面介绍其中一种: $f_x$表示$x$为根的子Hash值,$son_x$表示$x$的儿子结点...

weixin_30699465的博客 939

[JSOI2016]独特树叶

一、题目 点此看题 二、解法 其实就是同构,然后自然联想到了hashhashhash 简单介绍一下哈希的方法,我们先求出子hashhashhash值g[i]g[i]g[i],我用的是质数+++自然溢出的方式,设p[i]p[i]p[i]为第iii个质数,那么转移: g[u]=1+∑g[v]×p[siz[v]]g[u]=1+\sum g[v]\times p[siz[v]]g[u]=1+∑g[v]×p[siz[v]]那么对于uuu点为根的哈希值hs[u]hs[u]hs[u],就可以再跑一遍dfs

C202044zxy的博客 339

[BZOJ4754][Jsoi2016]独特树叶Hash+形DP+换根)

Address https://www.lydsy.com/JudgeOnline/problem.php?id=4754 Solution 先介绍一下有根的哈希: (1)一个节点的,哈希值为 111 。 (2)设 f[u]f[u]f[u] 为 uuu 的子的哈希值。先将 uuu 的所有子节点按照子哈希值从小到大排序,取底数 WWW 和模数 PPP ,设 viviv_i 是 ...

女装的你,如此好看! 720

JSOI2016】【hash】【形dp换根】独特树叶

【描述】 JYY 有两棵 A 和 B : A 有 N 个点,编号为 1 到 N ; B 有 N+1 个点,编号为 1 到 N+1 。 JYY 知道 B 恰好是由 A 加上一个叶节点,然后将节点的编号打乱后得到的。他想知道,这个多余的叶子到底是 B 中的哪一个叶节点呢? 【输入】 输入一行包含一个正整数 N。 接下来 N−1 行,描述 A ,每行包含两个整数表示 A 中的一条边; 接...

weixin_44111457的博客 285

哈希算法在同构方面的应用(下)

哈希算法在同构方面的应用 在上一篇文章中我们介绍了 枚举根节点哈希 和 求重心哈希 两种方法来断两棵无根是否同构。 但是如果有些题目中我必须要计算出每个根节点的 fff 值,且 n≤1e5n\le 1e5n≤1e5,我们要怎么办呢? 考虑以下问题。 (一):洛谷–P4323 [JSOI2016]独特树叶 题解: 考虑如下解法。 我们求解 AAA 中,以每一点 xxx 为根的 f[x]f[x]f[x] 值并保存。 我们枚举 BBB 中的叶子节点,计算删去这个叶子节点后,这个叶子节点的父亲的

_Griefs 363

Luogu P4323 [JSOI2016]独特树叶

一道比较好的Hash的题目,提供一种不一样的Hash方法。 首先无根同构断一般的做法只有Hash,所以不会的同学可以做了Luogu P5043 【模板】同构([BJOI2015]同构)再来。 首先我们直接考虑一种朴素的想法,暴力求出\(A\)中以每一个点为根时的Hash值 然后扔到一个set(你要再写个Hash也没事)里,再在\(B\)中枚举叶子节点,断去掉这个叶子...

weixin_30237281的博客 133

同构数怎么断_哈希算法在同构方面的应用(下)

哈希算法在同构方面的应用在上一篇文章中我们介绍了 枚举根节点哈希 和 求重心哈希 两种方法来断两棵无根是否同构。但是如果有些题目中我必须要计算出每个根节点的 值,且 ,我们要怎么办呢?考虑以下问题。(一):洛谷--P4323 [JSOI2016]独特树叶在这里插入图片描述题解:考虑如下解法。我们求解 中,以每一点 为根的 值并保存。我们枚举 中的叶子节点,计算...

weixin_39811193的博客 358

bzoj4754: [Jsoi2016]独特树叶

ORZ 下次做再写吧 注意hash的base不用太大不然容易挂 #include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<ma...

weixin_30426879的博客 89

洛谷 - P4323 [JSOI2016]独特树叶(上哈希+换根dp)

题目链接:点击查看 题目大意:给出一棵 n 个节点的 A ,再给出一棵 n + 1 个节点的 B,题目保证了 B 是 A 添加了一个叶子结点后的一棵,只不过编号的顺序不同,现在问这个叶子节点是哪个节点,如果有多个满足条件的节点,输出编号最小的那个 题目分析:题目的意思是 B 去掉一个叶子节点后,与 A 同构,这样我们可以先以点 1 为根节点,用上哈希 O( n ) 求出 dp1[ 1 ] 表示以点 1 为根节点时的哈希值,然后利用换根 dp ,O( n ) 求出 dp2[ x ] 表示

Falcon的博客 604

bzoj4754: [Jsoi2016]独特树叶 同构

bzoj4754: [Jsoi2016]独特树叶 Description JYY有两棵A和B:A有N个点,编号为1到N;B有N+1个点,编号为1到N+1。JYY知道B恰好是由A加上一个叶 节点,然后将节点的编号打乱后得到的。他想知道,这个多余的叶子到底是B中的哪一个叶节点呢? Input 输入一行包含一个正整数N。 接下来N-1行,描述A,每行包含两个整数表示...

maxtir的博客 791

同构简述

引入 是一种常见的数据结构。 我们把NNN个点,N−1N-1N−1条边的联通无向图称为。 对于两棵T1和T2T_1和T_2T1​和T2​,如果能够把T1T_1T1​的所有点重新标号,使得T1和T2T_1和T_2T1​和T2​完全相同,那么称这两棵同构的。也就是说,它们具有相同的形态。 下面介绍如何同构。 解决同构问题我们一般使用哈希.哈希方式很简单,根据下面这个式子来算: Fi=1+∑v∈{son[x]}f[v]∗prime[siz[v]]F_i=1+\sum_{v\in\{son[

小蒟蒻mlg的博客 1596

BZOJ4754 [Jsoi2016]独特树叶

标签:hash 题目 题目传送门 Description JYY有两棵A和B:A有N个点,编号为1到N;B有N+1个点,编号为1到N+1。JYY知道B恰好是由A加上一个叶节点,然后将节点的编号打乱后得到的。他想知道,这个多余的叶子到底是B中的哪一个叶节点呢? Input 输入一行包含一个正整数N。 接下来N-1行,描述A,每行包含两个整数表示A中的一条边;...

Welcome to yjjr's blog! 471

【BZOJ4754】独特树叶JSOI2016)-同构上哈希

测试地址:独特树叶 做法:本题需要用到同构上哈希。 问题的关键是如何快速断两棵同构。要想到一个确定的算法是很难的,因此我们考虑哈希。 考虑这样一种哈希方法:类似形DP,对于每个点,先递归求解它的儿子的子,然后把所有儿子按哈希值排序,然后在这个序列末尾加上一个子大小的数值,然后把这个序列按字符串哈希的方式求出哈希值,这就是当前子的哈希值。这样我们可以O(nlogn)O(nl...

Maxwei_wzj的OI世界 564

bzoj4754[JSOI2016]独特树叶

这个题....别人写得怎么都....那么短啊? 我怎么....WA了好几次啊....怎么去loj扒了数据才调出来啊? 这个算法...怎么我还是不知道对不对啊 怎么回事啊怎么回事啊怎么回事啊? 请无视上面的梦话 代码太长应该是因为我把两棵都有的函数通过复制改名实现,如果写成结构体大概能短一半吧 题意 给出两棵A和B,A有n个点,B有n+1个点,且B是由A的某个点上多连一个点再把节点重新标号得到的...

weixin_30369041的博客 144
上一篇: GDOI2016 总结
下一篇: HNOI2014 世界树 基于虚树的树形动态规划
YxuanwKeith
博客等级 码龄11年 154粉丝 143原创
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值