树哈希:
- 有根树:
\(H(u) = 1 + \sum_{v\in son_u}{H(v) \times prime_{size_v}}\)
其中 \(prime_i\) 表示第 \(i\) 个素数,\(size_u\) 表示 \(u\) 子树大小。
- 无根树:
考虑换根,转移简单,详见代码:
inline void dfs(int x,int fa) {ha[x]=sz[x]=1;for(auto y:G[x]) if(y^fa) dfs(y,x),sz[x]+=sz[y],ha[x]+=ha[y]*p[sz[y]];
}
inline void dfs2(int x,int fa) {for(auto y:G[x]) if(y^fa) dp[y]=(dp[x]-ha[y]*p[sz[y]])*p[n-sz[y]]+ha[y],dfs2(y,x);
}
为了避免被卡,简易将 \(prime\) 数组打乱后再哈希。
时间复杂度 \(O(n)\),较容易被卡。
AHU
需要递归处理子树并生成编码:
- 有根树:
map<vector<int>,int>id;
inline int dfs(int x,int fa) {vector<int>ch;for(auto y:G[x]) if(y^fa) ch.push_back(dfs(y,x));sort(ch.begin(),ch.end());if(!id.count(ch)) id[ch]=id.size()+1;return id[ch];
}
- 无根树:
先跑出树的重心,再跑 AHU。
时间复杂度 \(O(n log_2 n)\),无冲突。
总:AHU算法适用于对正确性要求极高的场景,而树哈希在效率和灵活性上更具优势。