From efb53150011244226ec0e1072db5e1e1ededd6cc Mon Sep 17 00:00:00 2001 From: 24OI-bot <15963390+24OI-bot@users.noreply.github.com> Date: Tue, 13 Aug 2019 08:12:03 -0400 Subject: [PATCH] style: format markdown files with remark-lint --- docs/graph/tree-hash.md | 51 +++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/docs/graph/tree-hash.md b/docs/graph/tree-hash.md index 147dedbd..24665fa7 100644 --- a/docs/graph/tree-hash.md +++ b/docs/graph/tree-hash.md @@ -26,7 +26,7 @@ $$ ![treehash1](./images/treehash1.png) -上图中,可以计算出两棵树的哈希值均为 $60(1+seed)$。 +上图中,可以计算出两棵树的哈希值均为 $60(1+seed)$ 。 ## Method II @@ -70,11 +70,11 @@ $$ $prime(i)$ 表示第 $i$ 个质数。 -主要参考了该篇博客 [树hash](https://www.cnblogs.com/huyufeifei/p/10817673.html)。 +主要参考了该篇博客 [树 hash](https://www.cnblogs.com/huyufeifei/p/10817673.html) 。 ## Example -### Problem [「BJOI2015」树的同构](https://www.luogu.org/problemnew/show/P5043) +### Problem [「BJOI2015」树的同构](https://www.luogu.org/problemnew/show/P5043) 我们用上述方式任选其一进行哈希,注意到我们求得的是子树的 hash 值,也就是说只有当根一样时同构的两棵子树 hash 值才相同。由于数据范围较小,我们可以暴力求出以每个点为根时的哈希值,也可以通过 up and down 树形 dp 的方式,遍历树两遍求出以每个点为根时的哈希值,排序后比较。 @@ -316,11 +316,11 @@ $$ int main() {} ``` -### Problem [HDu 6647](http://acm.hdu.edu.cn/showproblem.php?pid=6647) +### Problem [HDu 6647](http://acm.hdu.edu.cn/showproblem.php?pid=6647) 题目要求的是遍历一棵无根树产生的本质不同括号序列方案数。 -首先,注意到一个结论,对于两棵有根树,如果他们不同构,一定不会生成相同的括号序列。我们先考虑遍历有根树能够产生的本质不同括号序列方案数,假设我们当前考虑的子树根结点为 $u$,记 $f(u)$ 表示这棵子树的方案数,$son(u)$ 表示 $u$ 的儿子结点集合,从 $u$ 开始往下遍历,顺序可以随意选择,产生 $|son(u)|!$ 种排列,遍历每个儿子结点 $v$,$v$ 的子树内有 $f(v)$ 种方案,因此有 $f(u)=|son(u)|! \cdot \prod_{v\in son(u)} f(v)$。但是,同构的子树之间会产生重复,$f(u)$ 需要除掉每种本质不同子树出现次数阶乘的乘积,类似于多重集合的排列。 +首先,注意到一个结论,对于两棵有根树,如果他们不同构,一定不会生成相同的括号序列。我们先考虑遍历有根树能够产生的本质不同括号序列方案数,假设我们当前考虑的子树根结点为 $u$ ,记 $f(u)$ 表示这棵子树的方案数, $son(u)$ 表示 $u$ 的儿子结点集合,从 $u$ 开始往下遍历,顺序可以随意选择,产生 $|son(u)|!$ 种排列,遍历每个儿子结点 $v$ , $v$ 的子树内有 $f(v)$ 种方案,因此有 $f(u)=|son(u)|! \cdot \prod_{v\in son(u)} f(v)$ 。但是,同构的子树之间会产生重复, $f(u)$ 需要除掉每种本质不同子树出现次数阶乘的乘积,类似于多重集合的排列。 通过上述树形 dp,可以求出根结点的方案数,再通过 up and down 树形 dp,将父亲结点的哈希值和方案信息转移给儿子,可以求出以每个结点为根时的哈希值和方案数,每种不同的子树只需要计数一次即可。 @@ -330,22 +330,23 @@ $$ ??? "例题参考代码" ```cpp - #include + #include + #include #include #include - #include - #include - #include - #include - #include + #include #include + #include + #include + #include using namespace std; typedef long long ll; typedef unsigned long long ull; - typedef pair PII; + typedef pair PII; const int mod = 998244353; const int inf = 1 << 30; const int maxn = 100000 + 5; + ``` namespace sieve{ const int maxp = 2000000 + 5; @@ -413,14 +414,14 @@ $$ } using sieve::prime; using MyIO::gi; - + int ping[maxn], pingv[maxn]; - + int n, ans, siz[maxn]; vector edge[maxn]; map uqc[maxn]; map::iterator it; - + ull hashval[maxn], hashrt[maxn]; ull srchashval[maxn], srchashrt[maxn]; int dp[maxn], rdp[maxn]; @@ -440,10 +441,10 @@ $$ uqc[u][hashval[v]]++; hashval[u] += hashval[v] * prime[siz[v]]; } - + srchashval[u] = hashval[u]; hashval[u] = pack(hashval[u], siz[u]); - + dp[u] = mul(dp[u], ping[sz]); for (it = uqc[u].begin(); it != uqc[u].end(); it++) { dp[u] = mul(dp[u], pingv[it->second]); @@ -457,25 +458,25 @@ $$ } for (int v: edge[u]) { if (v == ff) continue; - + ull tmp = srchashrt[u] - hashval[v] * prime[siz[v]]; tmp = pack(tmp, n - siz[v]); - + uqc[v][tmp]++; srchashrt[v] = srchashval[v] + tmp * prime[n - siz[v]]; hashrt[v] = pack(srchashrt[v], n); - + int tdp = mul(rdp[u], inv(dp[v])); tdp = mul(tdp, inv((int)edge[u].size())); tdp = mul(tdp, uqc[u][hashval[v]]); rdp[v] = mul(dp[v], tdp); rdp[v] = mul(rdp[v], (int)edge[v].size()); rdp[v] = mul(rdp[v], inv(uqc[v][tmp])); - + dfs(v, u); } } - + int main() { sieve::init(); ping[0] = pingv[0] = 1; for (int i = 1; i < maxn; i++) { @@ -490,16 +491,16 @@ $$ edge[u].push_back(v); edge[v].push_back(u); } - + predfs(1, 0); ans = 0; qc.clear(); rdp[1] = dp[1]; hashrt[1] = hashval[1]; srchashrt[1] = srchashval[1]; dfs(1, 0); - + printf("%d\n", ans); - + for (int i = 1; i <= n; i++) { edge[i].clear(); uqc[i].clear(); -- 2.11.0