新技能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. (转)关于Linux核心转储文件 core dump

    所谓核心转储文件是内含进程终止时内存映像的一个文件.产生条件:特定的信号会引发进程创建一个核心转储文件并终止运行. 包括哪些特定信号,请参见http://man7.org/linux/man-page ...

  2. C#面向对象(二):封装和继承

    前文链接:C#面向对象(一):明确几个简单的概念作为开胃菜 面向对象开发有三大特性(特点 / 特征) : 封装, 继承, 多态.我们今天主要讨论封装和继承,多态会在下篇中讨论. 一.封装: 所谓封装, ...

  3. LeetCode Count Binary Substrings

    原题链接在这里:https://leetcode.com/problems/count-binary-substrings/description/ 题目: Give a string s, coun ...

  4. Yii错误404页面

    'errorHandler'=>array( // use 'site/error' action to display errors 'errorAction'=>YII_DEBUG ? ...

  5. centos7 node express项目 将http接口升级为https接口的解决方法

    1.将对应的ssl证书放到项目文件中(我装的镜像是oneinstack的,创建项目时可以直接选择生成ssl证书的项目,即 xxx.key 和 xxx.crt 文件): 2.修改bin/www文件,修改 ...

  6. Real-Time Rendering (2) - 变换和矩阵(Transforms and Matrics)

    http://blog.csdn.net/silangquan/article/details/9970673 提要 在图形的计算中,比如旋转.缩放.平移.投影等操作,矩阵都扮演着极其重要的角色,它是 ...

  7. java服务覆盖率统计 jacoco ant

    ● 下载jacoco.exec.jacocoant.jar.jacocoagent.jar ● jvm启动参数中添加(tomcat服务原理一样) JACOCO_OPTS=JAVA_OPTS=" ...

  8. (转)NHibernate各种数据库配置写法

    本文转载自:http://blog.csdn.net/hsg77/article/details/23463733 //NHibernate各种数据库连接参数文件配置方法说明 //配置文件Config ...

  9. 杂项:UUID

    ylbtech-杂项:UUID UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部 ...

  10. distinct可以用in代替(小技巧)

    distinct可以用in代替,in的好处是直接能获取所有数据,而distunct只能获取distinct的字段,不过效率肯定高一些.