Luogu 4323 [JSOI2016]独特的树叶
新技能get
树哈希,考虑到两棵树相同的条件,把每一个结点的哈希值和树的siz写进哈希值里去。
做出A树每一个结点为根时的树的哈希值丢进set中,然后暴力枚举B树中度数为1的点,求出删掉这个点之后的哈希值是否相同。
暴力算哈希是$O(n^{2})$的,考虑换根法,一个点作根的时候它的子树中的信息是不会变的,唯一的改变就是它的父亲及往上变成了它的新的一棵子树,这样我们可以递推出每一个结点的父亲作它的子树时的哈希值。
所以先自下到上哈希一遍,再重新自上到下算一遍,算父亲作儿子的哈希值就相当于挖掉一个子树,具体可以看代码实现。
因为哈希过程中的$sort$,时间复杂度为近似的$O(nlogn)$
感觉你谷评分好乱
Code:
#include <set> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef unsigned long long ull; const int N = 1e5 + 5; const ull sed = 12589; int m; ull q2[N], bin[N]; set <ull> s; struct Node { int now; ull val; Node (int x = 0, ull y = 0) : now(x), val(y) {} friend bool operator < (const Node &u, const Node &v) { return u.val < v.val; } } q1[N]; inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9'|| ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } struct Tree { int n, tot, head[N], fa[N], siz[N], deg[N]; ull v[N], f[N], rt[N], suf[N], pri[N]; struct Edge { int to, nxt; } e[N << 1]; inline void add(int from, int to) { e[++tot].to = to; e[tot].nxt = head[from]; head[from] = tot; } inline void addEdge(int x, int y) { deg[x]++, deg[y]++; add(x, y), add(y, x); } inline void init(int now) { n = now, tot = fa[1] = 0; for(int i = 1; i <= n; i++) head[i] = deg[i] = 0; for(int x, y, i = 1; i < n; i++) { read(x), read(y); addEdge(x, y); } } void dfs1(int x) { siz[x] = 1; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fa[x]) continue; fa[y] = x; dfs1(y); siz[x] += siz[y]; } int cnt = 0; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fa[x]) continue; q2[++cnt] = v[y]; } sort(q2 + 1, q2 + cnt + 1); v[x] = 0; for(int i = 1; i <= cnt; i++) v[x] = v[x] * sed + q2[i]; v[x] = v[x] * sed + (ull)siz[x]; } void dfs2(int x) { int cnt = 0; if(x > 1) q1[++cnt] = Node(fa[x], f[x]); for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fa[x]) continue; q1[++cnt] = Node(y, v[y]); } sort(q1 + 1, q1 + cnt + 1); pri[0] = 0; for(int i = 1; i <= cnt; i++) pri[i] = pri[i - 1] * sed + q1[i].val; suf[cnt + 1] = 0; for(int i = cnt; i >= 1; i--) suf[i] = suf[i + 1] + q1[i].val * bin[cnt - i]; for(int i = 1; i <= cnt; i++) { if(q1[i].now == fa[x]) continue; f[q1[i].now] = pri[i - 1] * bin[cnt - i] + suf[i + 1]; f[q1[i].now] = f[q1[i].now] * sed + (ull)(n - siz[q1[i].now]); } for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fa[x]) continue; dfs2(y); } } void calc() { dfs1(1), dfs2(1); for(int x = 1; x <= n; x++) { int cnt = 0; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fa[x]) continue; q2[++cnt] = v[y]; } if(x != 1) q2[++cnt] = f[x]; sort(q2 + 1, q2 + 1 + cnt); rt[x] = 0; for(int i = 1; i <= cnt; i++) rt[x] = rt[x] * sed + q2[i]; rt[x] = rt[x] * sed + (ull)n; } } } a, b; int main() { read(m); bin[0] = 1; for(int i = 1; i <= m + 2; i++) bin[i] = bin[i - 1] * sed; a.init(m), a.calc(); b.init(m + 1), b.calc(); /* for(int i = 1; i <= m; i++) printf("%llu ", a.rt[i]); printf("\n"); */ for(int i = 1; i <= m; i++) s.insert(a.rt[i]); for(int i = 1; i <= m + 1; i++) { if(b.deg[i] != 1) continue; if((i != 1 && s.find(b.f[i]) != s.end()) || (i == 1 && s.find(b.v[b.e[b.head[1]].to]) != s.end())) return printf("%d\n", i), 0; } return 0; }