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;
}
View Code

 

posted @ 2018-08-21 10:16  CzxingcHen  阅读(175)  评论(0编辑  收藏  举报