https://zybuluo.com/ysner/note/1177340

题面

有一颗大小为\(n\)的树\(A\),现加上一个节点并打乱编号,形成树\(B\),询问加上的节点最后编号是多少?

  • \(n\leq10^5\)

解析

判断树的同构显然需要树哈希。

可以先将树\(A\)中以每个节点为根的哈希值算出来存进一只\(unordered\_set\)中,

然后在树\(B\)中随便找一个不是叶节点的节点为根,枚举去掉一个叶节点,看根的\(Hash\)值是否能在\(unordered\_set\)中找到。

什么?只会\(O(n^2)\)求树的哈希值?

我们需要思考一种\(Hash\)函数,在根变动时,只影响新根和原根两节点的值,这样就可以每枚举到一个点,就算出其为根时的哈希值。因为要先\(DP\)一遍才能换根,所以复杂度为\(O(2n)\)。

并且函数需要很容易去掉某个点的影响。(异或)

\[Hash_{fa}=(Hash_{son1}+Base)\bigoplus(Hash_{son2}+Base)
\bigoplus ...+size_{fa}*p+1\]

一般树哈希还要考虑\(deep\),当然如果你要换根\(DP\),考虑\(deep\)的影响就没什么用啦。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<tr1/unordered_set>
#define ll unsigned long long
#define re register
#define il inline
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=5e5+10,p=1e9+7;
std::tr1::unordered_set<ll>Q;
int h[N],cnt,in[N],sz[N],n;
ll Hash[N],ans=1e18;
struct Edge{int to,nxt;}e[N<<1];
il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
il void dfs(re int u,re int fa)
{
sz[u]=1;re ll sum=0;
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
sz[u]+=sz[v];
Hash[u]=Hash[u]^(Hash[v]+17);
}
Hash[u]+=sz[u]*p+1;
}
il void dfs1(re int u,re int fa)
{
Q.insert(Hash[u]);
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to;
if(v==fa) continue;
re ll tmp=(Hash[u]-sz[u]*p-1)^(Hash[v]+17);
tmp+=(n-sz[v])*p+1;
Hash[v]-=sz[v]*p+1;
Hash[v]^=(tmp+17);
Hash[v]+=n*p+1;
sz[v]=n;
dfs1(v,u);
}
}
il void dfs2(re int u,re int fa)
{
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to;
if(v==fa) continue;
re ll tmp=(Hash[u]-sz[u]*p-1)^(Hash[v]+17);
if(in[v]>1)
{
tmp+=(sz[u]-sz[v])*p+1;
Hash[v]-=sz[v]*p+1;
Hash[v]^=(tmp+17);
Hash[v]+=sz[u]*p+1;
sz[v]=sz[u];
dfs2(v,u);
}
else
{
tmp+=(sz[u]-sz[v])*p+1;
if(Q.count(tmp)) ans=min(ans,1ull*v);
}
}
}
int main()
{
memset(h,-1,sizeof(h));
n=gi();
fp(i,1,n-1)
{
re int u=gi(),v=gi();
add(u,v);add(v,u);
}
dfs(1,0);//计算树A哈希值
dfs1(1,0);//换根算树A哈希值
memset(h,-1,sizeof(h));memset(Hash,0,sizeof(Hash));
fp(i,1,n)
{
re int u=gi(),v=gi();
++in[u];++in[v];
add(u,v);add(v,u);
}
re int i;
for(i=1;i<=n;i++) if(in[i]>1) break;//随便找一个不是叶节点的节点为根
dfs(i,0);//算树B哈希值
dfs2(i,0);//对叶子节点,试去掉它会怎么样;对非叶子节点,进行换根DP
printf("%lld\n",ans);
return 0;
}

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

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

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

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

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

  3. bzoj4754[JSOI2016]独特的树叶

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

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

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

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

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

  6. Luogu 4323 [JSOI2016]独特的树叶

    新技能get 树哈希,考虑到两棵树相同的条件,把每一个结点的哈希值和树的siz写进哈希值里去. 做出A树每一个结点为根时的树的哈希值丢进set中,然后暴力枚举B树中度数为1的点,求出删掉这个点之后的哈 ...

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

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

  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. CAD处理键盘被按下事件(com接口VB语言)

    主要用到函数说明: MxDrawXCustomEvent::KeyDown 键盘被按下,详细说明如下: 参数 说明 LONG lVk 是按钮码,如F8,的值为#define VK_F8 0x77 返回 ...

  2. Django - 数据获取

    Django - 数据获取 1.radio值获取 2.checkbox获取 3.select 获取 select 获取值,需要根据前端multiple来获取,get or getlist; 4.上传文 ...

  3. Delphi / Pascal 语法知识干货

    ********************************************* Pascal.Delph干货 *************************************** ...

  4. Dinic当前弧优化 模板及教程

    在阅读本文前,建议先自学最大流的Ek算法. 引入 Ek的核心是执行bfs,一旦找到增广路就停下来进行增广.换言之,执行一遍BFS执行一遍DFS,这使得效率大大降低.于是我们可以考虑优化. 核心思路 在 ...

  5. Maven中更改默认JDK版本

    只要在settings.xml文件中加上如下标签即可.(我这里是默认的1.7版本) <profiles> <profile> <id>jdk-1.7</id& ...

  6. render: h => h(App) $mount 什么意思

    初始一个vue.js项目时,常常发现main.js里有如下代码: new Vue({ render: h => h(App) }).$mount('#app') 这什么意思?那我自己做项目怎么改 ...

  7. Ubuntu 安装有道词典

    本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/51302546 官网首页:有道词典 其中 ...

  8. R语言为数据框添加列名或行名

    1.添加列名 wts=c(1,1,1) names(wts)=c("setosa","versicolor","virginica") 2. ...

  9. 使用javacv注意点

    由于Android开发需要,使用JavaCV,一开始我配置windows上的Javacv,发现总是出问题,说找不到dll文件.最终发现Opencv库必须解压在C盘根目录下才行. 这个有点坑爹,另外要注 ...

  10. LA 4794 状态DP+子集枚举

    状态压缩DP,把切割出的面积做状态压缩,统计出某状态下面积和. 设f(x,y,S)为在状态为S下在矩形x,y是否存在可能划分出S包含的面积.若S0是S的子集,对矩形x,y横切中竖切,对竖切若f(x,k ...