bzoj4754: [Jsoi2016]独特的树叶 树的同构

bzoj4754: [Jsoi2016]独特的树叶

Description

JYY有两棵树A和B:树A有N个点,编号为1到N;树B有N+1个点,编号为1到N+1。JYY知道树B恰好是由树A加上一个叶
节点,然后将节点的编号打乱后得到的。他想知道,这个多余的叶子到底是树B中的哪一个叶节点呢?

Input

输入一行包含一个正整数N。
接下来N-1行,描述树A,每行包含两个整数表示树A中的一条边;
接下来N行,描述树B,每行包含两个整数表示树B中的一条边。
1≤N≤10^5

Output

输出一行一个整数,表示树B中相比树A多余的那个叶子的编号。如果有多个符合要求的叶子,输出B中编号最小的
那一个的编号

Sample Input

5
1 2
2 3
1 4
1 5
1 2
2 3
3 4
4 5
3 6

Sample Output

1

分析

很神奇的一道树hash的题目。
首先以1 自底向上为根Hash一遍。我们可以得到每个子树的hash值。
然后仍然是以1为根,不过是 自顶向下 Hash一遍,这一回要得到的是子树父亲方向上的hash值。具体做法就是吧hash值中间挖掉一个子树即可,具体可以看代码。
然后凭借这子树和父亲方向上的hash值可以得到每个点作为根的hash值。
这样的话,我们对A做一遍上述过程,然后把以A的每个点为根的Hash值扔进set里面。对B枚举删去的叶子节点,判定其父亲方向上的值有没有在set里,如果有的话多余的叶子节点就是它咯

代码

/**************************************************************
    Problem: 4754
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:1152 ms
    Memory:19828 kb
****************************************************************/

#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 100005;
typedef unsigned long long ULL;
const ULL se = 12589;
int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
struct data {
    ULL a; int b;
    data(ULL c = 0, int d = 0) : a(c), b(d) {}
    bool operator < (data c) const {return a < c.a;}
}p[N];
ULL q[N], bin[N];
struct Tree {
    int nxt[N << 1], to[N << 1], pre[N], d[N], siz[N], fa[N], top, n;
    ULL v[N], rt[N], f[N], su[N], pr[N];
    void add(int u, int v) {to[++top] = v; nxt[top] = pre[u]; pre[u] = top;}
    void adds(int u, int v) {add(u, v); add(v, u); ++d[u]; ++d[v];}
    void init() {
        top = 0; for(int i = 1;i <= n; ++i) pre[i] = d[i] = 0; fa[1] = 0;
        for(int i = 1;i < n; ++i) adds(read(), read());
    }
    void dfs1(int u) {
        siz[u] = 1;
        for(int i = pre[u]; i; i = nxt[i]) 
        if(to[i] != fa[u]) {
            fa[to[i]] = u; dfs1(to[i]);
            siz[u] += siz[to[i]];
        }
        int k = 0;
        for(int i = pre[u]; i; i = nxt[i])
            if(to[i] != fa[u]) q[++k] = v[to[i]];
        sort(q + 1, q + k + 1); v[u] = 0;
        for(int i = 1;i <= k; ++i) v[u] = v[u] * se + q[i];
        v[u] = v[u] * se + ULL(siz[u]);
    }
    void dfs2(int u) {
        int k = 0;
        if(u > 1) p[++k] = data(f[u], fa[u]);
        for(int i = pre[u]; i; i = nxt[i]) 
        if(to[i] != fa[u])
            p[++k] = data(v[to[i]], to[i]);
        sort(p + 1, p + k + 1);
        pr[0] = 0; for(int i = 1;i <= k; ++i) pr[i] = pr[i - 1] * se + p[i].a;
        su[k + 1] = 0; for(int i = k;i >= 1; --i) su[i] = su[i + 1] + p[i].a * bin[k - i];
        for(int i = 1;i <= k; ++i) 
        if(p[i].b != fa[u]) {
            f[p[i].b] = pr[i - 1] * bin[k - i] + su[i + 1];
            f[p[i].b] = f[p[i].b] * se + (ULL)(n - siz[p[i].b]);
        }
        for(int i = pre[u]; i; i = nxt[i]) if(to[i] != fa[u]) dfs2(to[i]);
    }
    void calc() {
        dfs1(1); dfs2(1);
        for(int u = 1;u <= n; ++u) {
            int k = 0;
            for(int i = pre[u]; i; i = nxt[i])
                if(to[i] != fa[u]) q[++k] = v[to[i]];
            if(fa[u]) q[++k] = f[u];
            sort(q + 1, q + k + 1);
            rt[u] = 0;
            for(int i = 1;i <= k; ++i) rt[u] = rt[u] * se + q[i];
            rt[u] = rt[u] * se + ULL(n); 
        }
    }
}a, b;
sets;
int main() {
    int m = read();
    bin[0] = 1; for(int i = 1;i <= m + 2; ++i) bin[i] = bin[i - 1] * se;
    a.n = m; a.init(); a.calc();
    b.n = m + 1; b.init(); b.calc();
    for(int i = 1;i <= m; ++i) s.insert(a.rt[i]);
    for(int i = 1;i <= m + 1; ++i) 
        if(b.d[i] == 1 && ((i != 1 && s.find(b.f[i]) != s.end()) 
        || (i == 1 && s.find(b.v[b.to[b.pre[1]]]) != s.end())) ) 
            return 0 * printf("%d\n", i);
    return 0;
}
/*
10
1 2
1 3
2 4
4 10
3 5
5 7
5 8
3 6
6 9
2 1
1 4
4 10
2 3
3 5
3 6
6 9
5 7
5 8
10 11
*/


你可能感兴趣的:(树上操作-树hash)