新技能get

树哈希,考虑到两棵树相同的条件,把每一个结点的哈希值和树的siz写进哈希值里去。

做出A树每一个结点为根时的树的哈希值丢进set中,然后暴力枚举B树中度数为1的点,求出删掉这个点之后的哈希值是否相同。

暴力算哈希是$O(n^{2})$的,考虑换根法,一个点作根的时候它的子树中的信息是不会变的,唯一的改变就是它的父亲及往上变成了它的新的一棵子树,这样我们可以递推出每一个结点的父亲作它的子树时的哈希值。

所以先自下到上哈希一遍,再重新自上到下算一遍,算父亲作儿子的哈希值就相当于挖掉一个子树,具体可以看代码实现。

因为哈希过程中的$sort$,时间复杂度为近似的$O(nlogn)$

感觉你谷评分好乱

Code:

#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ull; const int N = 1e5 + ;
const ull sed = ; int m;
ull q2[N], bin[N];
set <ull> s; struct Node {
int now;
ull val; Node (int x = , ull y = ) : now(x), val(y) {} friend bool operator < (const Node &u, const Node &v) {
return u.val < v.val;
} } q1[N]; inline void read(int &X) {
X = ;
char ch = ;
int op = ;
for(; ch > ''|| ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} struct Tree {
int n, tot, head[N], fa[N], siz[N], deg[N];
ull v[N], f[N], rt[N], suf[N], pri[N]; struct Edge {
int to, nxt;
} e[N << ]; inline void add(int from, int to) {
e[++tot].to = to;
e[tot].nxt = head[from];
head[from] = tot;
} inline void addEdge(int x, int y) {
deg[x]++, deg[y]++;
add(x, y), add(y, x);
} inline void init(int now) {
n = now, tot = fa[] = ;
for(int i = ; i <= n; i++)
head[i] = deg[i] = ;
for(int x, y, i = ; i < n; i++) {
read(x), read(y);
addEdge(x, y);
}
} void dfs1(int x) {
siz[x] = ;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fa[x]) continue;
fa[y] = x;
dfs1(y);
siz[x] += siz[y];
} int cnt = ;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fa[x]) continue;
q2[++cnt] = v[y];
} sort(q2 + , q2 + cnt + );
v[x] = ;
for(int i = ; i <= cnt; i++)
v[x] = v[x] * sed + q2[i];
v[x] = v[x] * sed + (ull)siz[x];
} void dfs2(int x) {
int cnt = ;
if(x > ) q1[++cnt] = Node(fa[x], f[x]);
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fa[x]) continue;
q1[++cnt] = Node(y, v[y]);
} sort(q1 + , q1 + cnt + );
pri[] = ;
for(int i = ; i <= cnt; i++)
pri[i] = pri[i - ] * sed + q1[i].val;
suf[cnt + ] = ;
for(int i = cnt; i >= ; i--)
suf[i] = suf[i + ] + q1[i].val * bin[cnt - i]; for(int i = ; i <= cnt; i++) {
if(q1[i].now == fa[x]) continue;
f[q1[i].now] = pri[i - ] * bin[cnt - i] + suf[i + ];
f[q1[i].now] = f[q1[i].now] * sed + (ull)(n - siz[q1[i].now]);
} for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fa[x]) continue;
dfs2(y);
}
} void calc() {
dfs1(), dfs2();
for(int x = ; x <= n; x++) {
int cnt = ;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fa[x]) continue;
q2[++cnt] = v[y];
}
if(x != ) q2[++cnt] = f[x]; sort(q2 + , q2 + + cnt);
rt[x] = ;
for(int i = ; i <= cnt; i++)
rt[x] = rt[x] * sed + q2[i];
rt[x] = rt[x] * sed + (ull)n;
}
} } a, b; int main() {
read(m); bin[] = ;
for(int i = ; i <= m + ; i++)
bin[i] = bin[i - ] * sed; a.init(m), a.calc();
b.init(m + ), b.calc(); /* for(int i = 1; i <= m; i++)
printf("%llu ", a.rt[i]);
printf("\n"); */ for(int i = ; i <= m; i++)
s.insert(a.rt[i]); for(int i = ; i <= m + ; i++) {
if(b.deg[i] != ) continue;
if((i != && s.find(b.f[i]) != s.end())
|| (i == && s.find(b.v[b.e[b.head[]].to]) != s.end()))
return printf("%d\n", i), ;
} return ;
}

Luogu 4323 [JSOI2016]独特的树叶的更多相关文章

  1. Luogu P4323 [JSOI2016]独特的树叶

    一道比较好的树Hash的题目,提供一种不一样的Hash方法. 首先无根树的同构判断一般的做法只有树Hash,所以不会的同学可以做了Luogu P5043 [模板]树同构([BJOI2015]树的同构) ...

  2. BZOJ4754 & 洛谷4323 & LOJ2072:[JSOI2016]独特的树叶——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4754 https://www.luogu.org/problemnew/show/P4323 ht ...

  3. BZOJ 4754 [JSOI2016]独特的树叶 | 树哈希判同构

    题目链接 这道题是一道判断无根树同构的模板题,判断同构主要的思路就是哈希. 一遇到哈希题,一百个人能有一百零一种哈希方式,这篇题解随便选用了一种--类似杨弋<Hash在信息学竞赛中的一类应用&g ...

  4. bzoj4754[JSOI2016]独特的树叶

    这个题....别人写得怎么都....那么短啊? 我怎么....WA了好几次啊....怎么去loj扒了数据才调出来啊? 这个算法...怎么我还是不知道对不对啊 怎么回事啊怎么回事啊怎么回事啊? 请无视上 ...

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

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

  6. P4323 [JSOI2016]独特的树叶(树哈希)

    传送门 树哈希?->这里 反正大概就是乱搞--的吧-- //minamoto #include<bits/stdc++.h> #define R register #define l ...

  7. [JSOI2016]独特的树叶

    https://zybuluo.com/ysner/note/1177340 题面 有一颗大小为\(n\)的树\(A\),现加上一个节点并打乱编号,形成树\(B\),询问加上的节点最后编号是多少? \ ...

  8. bzoj 4754: [Jsoi2016]独特的树叶

    不得不说这是神题. %%%   http://blog.csdn.net/samjia2000/article/details/51762811 #include <cstdio> #in ...

  9. 【BZOJ4754】独特的树叶(哈希)

    [BZOJ4754]独特的树叶(哈希) 题面 BZOJ 给定一个\(n\)个节点的树A和一个\(n+1\)个节点的树\(B\) 求\(B\)的一个编号最小的节点,使得删去这个节点后\(A,B\)同构 ...

随机推荐

  1. New Concept English three (46)

    27w/m 66 error So great is our passion for doing things for ourselves, that we are becoming increasi ...

  2. mysql更改数据文件目录及my.ini位置。

    需求:更改mysql数据数据文件目录及my.ini位置. 步骤: 1.查找my.ini位置,可通过windows服务所对应mysql启动项,查看其对应属性->可执行文件路径,获取my.ini路径 ...

  3. boost库 bind/function的使用

    Boost::Function 是对函数指针的对象化封装,在概念上与广义上的回调函数类似.相对于函数指针,function除了使用自由函数,还可以使用函数对象,甚至是类的成员函数,这个就很强大了哈 # ...

  4. Java内存垃圾回收机制(转贴)

    Java的堆是一个运行时数据区,类的实例(对象)从中分配空间.Java虚拟机(JVM)的堆中储存着正在运行的应用程序所建立的所有对象,这些对象通 过new.newarray.anewarray和mul ...

  5. deque容器

    一.deque容器基本概念 deque是“double-ended queue”的缩写,和vector一样,deque也支持随机存取.vector是单向开口的连续性空间,deque则是一种双向开口的连 ...

  6. 【转】Java内存与垃圾回收调优

    要了解Java垃圾收集机制,先理解JVM内存模式是非常重要的.今天我们将会了解JVM内存的各个部分.如何监控以及垃圾收集调优. Java(JVM)内存模型 正如你从上面的图片看到的,JVM内存被分成多 ...

  7. 多版本python管理miniconda(集成了virtualenv和pip功能)

    miniconda下载地址: https://conda.io/docs/user-guide/install/index.html Installing on Linux Download the ...

  8. Java-API-Package:org.springframework.stereotype

    ylbtech-Java-API-Package:org.springframework.stereotype 1.返回顶部 1. @NonNullApi @NonNullFields Package ...

  9. MyBatis----延迟加载demo

    一:创建数据库脚本drop table project_info; drop table status; create table status( id number(10) primary key, ...

  10. PDM后续处理-驼峰规则、清除约束、外键改名

    Option   Explicit ValidationMode   =   True InteractiveMode =   im_Batch Dim   mdl   '当前model '获取当前活 ...