Luogu 4323 [JSOI2016]独特的树叶

时间:2022-05-16 16:36:27

新技能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 + ;
const ull sed = ; int m;
ull q2[N], bin[N];
set <ull> s; struct Node {
int now;
ull val; Node (int x = , ull y = ) : 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 = ;
char ch = ;
int op = ;
for(; ch > ''|| ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
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 << ]; 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[] = ;
for(int i = ; i <= n; i++)
head[i] = deg[i] = ;
for(int x, y, i = ; i < n; i++) {
read(x), read(y);
addEdge(x, y);
}
} void dfs1(int x) {
siz[x] = ;
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 = ;
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 + , q2 + cnt + );
v[x] = ;
for(int i = ; i <= cnt; i++)
v[x] = v[x] * sed + q2[i];
v[x] = v[x] * sed + (ull)siz[x];
} void dfs2(int x) {
int cnt = ;
if(x > ) 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 + , q1 + cnt + );
pri[] = ;
for(int i = ; i <= cnt; i++)
pri[i] = pri[i - ] * sed + q1[i].val;
suf[cnt + ] = ;
for(int i = cnt; i >= ; i--)
suf[i] = suf[i + ] + q1[i].val * bin[cnt - i]; for(int i = ; i <= cnt; i++) {
if(q1[i].now == fa[x]) continue;
f[q1[i].now] = pri[i - ] * bin[cnt - i] + suf[i + ];
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(), dfs2();
for(int x = ; x <= n; x++) {
int cnt = ;
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 != ) q2[++cnt] = f[x]; sort(q2 + , q2 + + cnt);
rt[x] = ;
for(int i = ; i <= cnt; i++)
rt[x] = rt[x] * sed + q2[i];
rt[x] = rt[x] * sed + (ull)n;
}
} } a, b; int main() {
read(m); bin[] = ;
for(int i = ; i <= m + ; i++)
bin[i] = bin[i - ] * sed; a.init(m), a.calc();
b.init(m + ), b.calc(); /* for(int i = 1; i <= m; i++)
printf("%llu ", a.rt[i]);
printf("\n"); */ for(int i = ; i <= m; i++)
s.insert(a.rt[i]); for(int i = ; i <= m + ; i++) {
if(b.deg[i] != ) continue;
if((i != && s.find(b.f[i]) != s.end())
|| (i == && s.find(b.v[b.e[b.head[]].to]) != s.end()))
return printf("%d\n", i), ;
} return ;
}