题目链接

这道题是一道判断无根树同构的模板题,判断同构主要的思路就是哈希。

一遇到哈希题,一百个人能有一百零一种哈希方式,这篇题解随便选用了一种——类似杨弋《Hash在信息学竞赛中的一类应用》中的这种,可能不是最简洁好写的,但是能用。

我的哈希规则:子树\(u\)的哈希值由它的每一个子树\(v_i\)的哈希值得来,首先将所有\(f(v)\)排个序(防止顺序不同造成影响),然后\(f(u) = size(u) * \sum_i f(v_i)W^{i - 1} \bmod P\),\(W\)是事先选取的一个位权,\(P\)是模数,\(size(u)\)是子树\(u\)的大小。

这样DFS一遍可求出以\(1\)号节点为根时,所有子树的哈希值\(f(u)\)。

但是这是无根树,我们想求出以任意节点为根时整棵树的哈希值。

设\(fa_u\)为以\(1\)为根时\(u\)的父亲,则上面的\(f(u)\)也是以\(fa_u\)为根时子树\(u\)的哈希值。

再求一个\(g(u)\)表示以\(u\)为根时子树\(fa_u\)的哈希值。这个\(g(u)\)怎么求呢?再DFS一遍,对于每个节点,\(g(u)\)由\(g(fa_u)\)以及\(u\)的每个兄弟\(v_i\)的\(f(v_i)\)得来。但是直接暴力枚举的话在菊花图上是\(O(n^2)\)的,那怎么办呢?

对于每个节点\(u\)维护一个数组,存储它所有儿子的哈希值\(f(v)\),如果有父亲,则\(g(u)\)也在里面,把这个数组排好序,求出每个前缀的哈希值和每个后缀的哈希值。这时,以\(u\)为根时整棵树的哈希值就是整个数组的哈希值(再乘上子树大小\(n\))。

此时求每个儿子\(v\)的\(g(v)\),就是从那个数组中间去掉\(f(v)\)后的哈希值,二分查找后把前缀哈希值和后缀哈希值拼起来就可以得到。记得乘上\(v\)为根时\(u\)的\(size\)即\(n - size(v)\)。

这样就求出以每个节点为根的哈希值了。

把A的所有哈希值存到一个set里,然后枚举B的每个度为1的点\(u\),求出以\(u\)为根它的唯一子树\(v\)的哈希值,如果set里有这个值,\(u\)就是所求的点之一。

代码比较丑,见谅 ><

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <set>
#define space putchar(' ')
#define enter putchar('\n')
typedef long long ll;
using namespace std;
template <class T>
void read(T &x){char c;bool op = 0;while(c = getchar(), c < '0' || c > '9')if(c == '-') op = 1;x = c - '0';while(c = getchar(), c >= '0' && c <= '9')x = x * 10 + c - '0';if(op) x = -x;
}
template <class T>
void write(T x){if(x < 0) putchar('-'), x = -x;if(x >= 10) write(x / 10);putchar('0' + x % 10);
}const int N = 100005, W = 1000000021, P = 999999137;
int n, m, fa[N], f[N], g[N], pw[N], Sze[N], deg[N], ans = P;
int ecnt, adj[N], nxt[2*N], go[2*N];
vector <int> son[N], sl[N], sr[N];
set <int> vis;
bool isB;void add(int u, int v){if(isB) deg[u]++;go[++ecnt] = v;nxt[ecnt] = adj[u];adj[u] = ecnt;
}
int dfs1(int u, int pre){Sze[u] = 1;fa[u] = pre;son[u].clear();for(int e = adj[u], v; e; e = nxt[e])if((v = go[e]) != pre){son[u].push_back(dfs1(v, u));Sze[u] += Sze[v];}if(son[u].empty()) return f[u] = 1;sort(son[u].begin(), son[u].end());ll ret = 0;for(int i = 0; i < (int)son[u].size(); i++)ret = (ret * W + son[u][i]) % P;return f[u] = Sze[u] * ret % P;
}void dfs2(int u){if(fa[u]){son[u].push_back(g[u]);sort(son[u].begin(), son[u].end());}int sze = son[u].size();sl[u].resize(sze);sl[u][0] = son[u][0];for(int i = 1; i < sze; i++)sl[u][i] = ((ll)sl[u][i - 1] * W + son[u][i]) % P;sr[u].resize(sze);sr[u][sze - 1] = son[u][sze - 1];for(int i = sze - 2; i >= 0; i--)sr[u][i] = (sr[u][i + 1] + (ll)son[u][i] * pw[sze - i - 1]) % P;for(int e = adj[u], v; e; e = nxt[e])if((v = go[e]) != fa[u]){if(sze == 1){g[v] = 1;dfs2(v);break;}int p = lower_bound(son[u].begin(), son[u].end(), f[v]) - son[u].begin();g[v] = 0;if(p + 1 < sze) g[v] = sr[u][p + 1];if(p - 1 >= 0) g[v] = (g[v] + (ll)sl[u][p - 1] * pw[sze - 1 - p]) % P;g[v] = (ll)g[v] * (n - Sze[v]) % P;if(isB && deg[v] == 1 && vis.find(g[v]) != vis.end()) ans = min(ans, v);dfs2(v);}if(!isB) vis.insert((ll)sl[u][sze - 1] * n % P);
}int main(){pw[0] = 1;for(int i = 1; i < N; i++)pw[i] = (ll)pw[i - 1] * W % P;read(n);for(int i = 1, u, v; i < n; i++)read(u), read(v), add(u, v), add(v, u);dfs1(1, 0);dfs2(1);ecnt = 0, isB = 1, n++;memset(adj, 0, sizeof(adj));for(int i = 1, u, v; i < n; i++)read(u), read(v), add(u, v), add(v, u);dfs1(1, 0);if(deg[1] == 1 && vis.find(f[go[adj[1]]]) != vis.end())ans = 1;dfs2(1);write(ans), enter;return 0;
}

转载于:https://www.cnblogs.com/RabbitHu/p/9165770.html

BZOJ 4754 [JSOI2016]独特的树叶 | 树哈希判同构相关推荐

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

    题目链接:点击查看 题目大意:给出一棵 n 个节点的树 A ,再给出一棵 n + 1 个节点的树 B,题目保证了树 B 是树 A 添加了一个叶子结点后的一棵树,只不过编号的顺序不同,现在问这个叶子节点 ...

  2. 【BZOJ - 4754】独特的树叶(树哈希)

    题干: JYY有两棵树A和B:树A有N个点,编号为1到N:树B有N+1个点,编号为1到N+1.JYY知道树B恰好是由树A加上一个叶 节点,然后将节点的编号打乱后得到的.他想知道,这个多余的叶子到底是树 ...

  3. BZOJ4754 JSOI2016独特的树叶(哈希)

    判断两棵无根树是否同构只需要把重心提作根哈希即可.由于只添加了一个叶子,重心的位置几乎不发生偏移,所以直接把两棵树的重心提起来,逐层找哈希值不同且对应的两子树即可.被一个普及组子问题卡一年. #inc ...

  4. P5043 【模板】树同构([BJOI2015]树的同构 // P4323 [JSOI2016]独特的树叶

    一.P5043 [模板]树同构([BJOI2015]树的同构): #include<iostream> #include<cstdio> #include<algorit ...

  5. 树哈希判断同构无根同构问题转有根同构问题

    前言 判断无根的同构 利用重心作为根进行dfs处理 注意哈希的公式: f[fa]=∑f[son]*primesiz[fa] 这个东西好像也是千变万化 复杂度:nmlogn 代码 #include< ...

  6. BZOJ.4337.[BJOI2015]树的同构(树哈希)

    BZOJ 洛谷 \(Description\) 给定\(n\)棵无根树.对每棵树,输出与它同构的树的最小编号. \(n及每棵树的点数\leq 50\). \(Solution\) 对于一棵无根树,它的 ...

  7. bzoj 3685 普通van Emde Boas树(坑)

    3685: 普通van Emde Boas树 Time Limit: 9 Sec Memory Limit: 128 MB Submit: 1758 Solved: 563 [Submit][Stat ...

  8. bzoj 3162: 独钓寒江雪 树哈希+树形dp

    题意 给出一棵无标号无根树,问本质不同的最大独立集数量.答案模1e9+7. n<=500000 分析 对于一般的情况,我们可以先找出树的重心作为根,然后进行树形dp.这样做有什么好处呢?通过根的 ...

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

    树的同构 problem solution code solution code problem 模板题 solution Ⅰ. 最小表示法 将树转化为 0/10/10/1 括号序列:从根开始 dfs ...

  10. 洛谷4895 BZOJ3162 独钓寒江雪 树形dp 树哈希

    题目链接 题意: 给定一棵无根树,求其中本质不同的独立集的个数.独立集就是一个集合中的点之间都没有边直接相连.n<=5e5n<=5e5n<=5e5,对1e9+71e9+71e9+7取 ...

最新文章

  1. 怎么修改Outlook2010 PST数据文件位置(默认邮件存储目录)
  2. 【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 为 二级指针 分配内存 - 存放 一维指针 | 为每个 一级指针 分配内存 | 释放二维指针内存 )
  3. Berkeley DB——Records
  4. php domdocument getattribute,PHP DOMElement getAttribute()用法及代码示例
  5. 乐Phone:还原联想的联想
  6. sql2008 cet查询 所有层级_案例 | CET助力一汽实现动力设备系统智慧管理
  7. CSDN Markdown使用
  8. js基础练习---图片无缝左右滚动效果(主要以复制删除为主)
  9. 从行业应用到智慧城市,升哲科技Alpha协议如何保障物理世界的数据传输
  10. python第六课——判断结构
  11. python_基础部分(1)
  12. Python实用: 让桌面壁纸每日自动更新为必应首页图片
  13. 2019年成功与失败的危机公关案例分析
  14. 以太网网卡历史-3com,intel及其它
  15. Qt在VS中的使用方法详解
  16. 毕业五年,想当初若没有入行测试,我是否还会如这般焦虑
  17. USB键盘在DOS下无法用,在BIOS和WIN中正常,奇怪情况,原来是PS2接口有问题
  18. c语言蒙特卡洛树搜索代码,uct-gobang
  19. 东北大学计算机硬件题库,东北大学计算机硬件基础机考题库(150单选+编程).doc...
  20. 数据库服务器审计系统部署方式,数据库审计系统D2020,agent方式审计如何配置...

热门文章

  1. session 分布式处理-----https://segmentfault.com/a/1190000013447750?utm_source=tag-newest
  2. nginx logrotate 日志切割
  3. 识别验证码,你有几分成功率?
  4. redhat solutions
  5. Windows Hook机制
  6. 春节7天新增病毒54万 钓鱼欺诈成最大威胁
  7. 【BMC_patrol常见问题汇总】Console License生成
  8. cisco 设备的NTP客户端设置
  9. 哪些人可以报考公务员 哪些人不能报考公务员
  10. 笛卡尔坐标系_笛卡儿坐标系