HYSBZOJ 4754 [Jsoi2016]独特的树叶
题目大意
◇题目传送门◆
分析
这道题显然考察的是树哈希。
我这里采用的方法是:
f(u)=1+∑v∈son(u)f(v)×Psizvf(u)=1+\sum_{v\in son(u)}f(v)\times P_{siz_v} f(u)=1+v∈son(u)∑?f(v)×Psizv??
其中PPP表示质数序列,sizvsiz_vsizv?表示以vvv为根的子树的大小,f(u)f(u)f(u)表示以uuu为根的子树的哈希值。
现在的要求就是对于AAA和BBB中的每个节点,求出以该节点为根的整棵树的哈希值。
这样的话,我们考虑换根。
设g(u)g(u)g(u)为去掉uuu这棵子树后的整棵树的哈希值,不难得出:
g(u)=h(u)?f(u)×Psizug(u)=h(u)-f(u)\times P_{siz_u} g(u)=h(u)?f(u)×Psizu??
其中h(u)h(u)h(u)表示以当前节点为根,整棵树的哈希值。h(u)h(u)h(u)的表达式我们也不难得出:
h(u)={f(u)u=1f(u)+g(u)×Psiz1?sizuu≠1h(u)=\begin{cases}f(u)&u=1\\f(u)+g(u)\times P_{siz_1-siz_u}&u\neq1\end{cases} h(u)={ f(u)f(u)+g(u)×Psiz1??sizu???u=1u??=1?
这里我用节点111为根。
然后我们将BBB的所有哈希值塞进set
或者map
里面去。再枚举AAA中的每一个点,将它接到一个虚拟的点上面去,算出新的哈希值1+h(u)×PN1+h(u)\times P_N1+h(u)×PN?,找到最小的那个节点就是了。
总时间复杂度为O(Nlog?N)O(N\log N)O(NlogN)。
参考代码
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;typedef unsigned long long ull;
const int Maxn = 1e5;
const int P = 1e7;ull SEED[Maxn + 5];
void calcSEED() {
static bool ispri[P + 5];int cnt = 0;for(int i = 2; i <= P; i++) {
if(!ispri[i])SEED[++cnt] = i;for(int j = 1; i * SEED[j] <= P && j <= cnt; j++) {
ispri[i * SEED[j]] = true;if(i % SEED[j] == 0) break;}}
}struct Graph {
struct Edge {
int to;Edge *nxt;};Edge pool[Maxn * 2 + 5];Edge *G[Maxn + 5], *ecnt;void init() {
memset(G, 0, sizeof G);ecnt = &pool[0];}void addedge(int u, int v) {
Edge *p = ++ecnt;p->to = v, p->nxt = G[u];G[u] = p;}ull hashval[Maxn + 5];ull f[Maxn + 5], g[Maxn + 5];int siz[Maxn + 5];void dfs1(int u, int fa) {
f[u] = siz[u] = 1; for(Edge *p = G[u]; p != NULL; p = p->nxt) {
int v = p->to;if(v == fa) continue;dfs1(v, u);siz[u] += siz[v], f[u] += SEED[siz[v]] * f[v];}}void dfs2(int u, int fa) {
if(fa != 0) g[u] = hashval[fa] - f[u] * SEED[siz[u]];if(fa == 0) hashval[u] = f[u];else hashval[u] = f[u] + g[u] * SEED[siz[1] - siz[u]];for(Edge *p = G[u]; p != NULL; p = p->nxt) {
int v = p->to;if(v == fa) continue;dfs2(v, u);}}void calc_hash() {
dfs1(1, 0);dfs2(1, 0);}
};int N;Graph A, B;
map<ull, int> s;
int main() {
#ifdef LOACLfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifcalcSEED();A.init(), B.init();scanf("%d", &N);for(int i = 1; i < N; i++) {
int u, v;scanf("%d %d", &u, &v);A.addedge(u, v), A.addedge(v, u);}for(int i = 1; i <= N; i++) {
int u, v;scanf("%d %d", &u, &v);B.addedge(u, v), B.addedge(v, u);}A.calc_hash(), B.calc_hash();for(int i = 1; i <= N + 1; i++)if(!s.count(B.hashval[i])) s[B.hashval[i]] = i;else s[B.hashval[i]] = min(s[B.hashval[i]], i);for(int i = 1; i <= N; i++)if(s.count(1 + A.hashval[i] * SEED[N])) {
printf("%d\n", s[1 + A.hashval[i] * SEED[N]]);break;}return 0;
}