题目链接

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

一遇到哈希题,一百个人能有一百零一种哈希方式,这篇题解随便选用了一种——类似杨弋《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;
}

BZOJ 4754 [JSOI2016]独特的树叶 | 树哈希判同构的更多相关文章

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

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

  2. bzoj4337: BJOI2015 树的同构 树哈希判同构

    题目链接 bzoj4337: BJOI2015 树的同构 题解 树哈希的一种方法 对于每各节点的哈希值为hash[x] = hash[sonk[x]] * p[k]; p为素数表 代码 #includ ...

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

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

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

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

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

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

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

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

  7. bzoj4754[JSOI2016]独特的树叶

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

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

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

  9. [JSOI2016]独特的树叶

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

随机推荐

  1. How to Enable TLS 1.2 on Windows Server 2008 R2 and IIS 7.5

    Nowadays there is an SSL vulnerability called POODLE discovered by Google team in SSLv3 protocol. So ...

  2. [Spark][Python][DataFrame][SQL]Spark对DataFrame直接执行SQL处理的例子

    [Spark][Python][DataFrame][SQL]Spark对DataFrame直接执行SQL处理的例子 $cat people.json {"name":" ...

  3. Azure Load Balancer : 支持 IPv6

    越来越多的网站开始支持 IPv6,即使是哪些只提供 api 服务的站点也需要支持 IPv6,比如苹果应用商店中的 app 早就强制要求服务器端支持 IPv6 了.笔者在前文<Azure Load ...

  4. SpringMvc返回Json调试

    spring-web-5.0.6.RELEASE.jar!/org/springframework/web/method/support/HandlerMethodReturnValueHandler ...

  5. VS2013安装和单元测试

    1. VC2013安装过程及使用感受 刚上大一的时候老师推荐我们用VC++6.0.当时也就听了老师的话用VC++6.0编程了一段时间.后来上了大二买了电脑VC++6.0支持不了WIN8.1所以我就开始 ...

  6. Daily Scrumming* 2015.12.20(Day 12)

    一.团队scrum meeting照片 二.成员工作总结 姓名 任务ID 迁入记录 江昊 任务1090 https://github.com/buaaclubs-team/temp-front/com ...

  7. beta 答辩总结

    评审表 编号 团队名称 项目名称 格式 内容 PPT 演讲 答辩 总分/100 1 天机组 指尖加密 14 13 13 13 13 66 2 PMS Your eyes 14 14 17 14 15 ...

  8. scipy的一些函数名

    rvs:随机变量pdf:概率密度函数cdf:累计分布函数sf:残存函数(1-CDF)ppf:分位点函数(CDF的逆)isf:逆残存函数(sf的逆)stats:返回均值,方差,(费舍尔)偏态,(费舍尔) ...

  9. 重载(overload)、覆盖(override)、隐藏(hide)的区别

    http://blog.csdn.net/yanjun_1982/archive/2005/09/02/470405.aspx 重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同.调用的 ...

  10. activiti engine.schema.update DB_SCHEMA_UPDATE_FALSE

    engine.properties # engine propertiesengine.schema.update=trueengine.activate.jobexecutor=falseengin ...