【BZOJ4754】独特的树叶(JSOI2016)-树同构:树上哈希

测试地址:独特的树叶
做法:本题需要用到树同构:树上哈希。
问题的关键是如何快速判断两棵树同构。要想到一个确定的算法是很难的,因此我们考虑哈希。
考虑这样一种哈希方法:类似树形DP,对于每个点,先递归求解它的儿子的子树,然后把所有儿子按哈希值排序,然后在这个序列末尾加上一个子树大小的数值,然后把这个序列按字符串哈希的方式求出哈希值,这就是当前子树的哈希值。这样我们可以O(nlogn)求出以某个点为根的哈希值。于是我们如果我们求出一棵树中所有点为根的哈希值,存在map里,再用另一棵树某一个点的哈希值去map里找,就可以判断两棵树是否同构了。
然而暴力从每个点向下求哈希值是O(n2logn)的,无法通过。我们可以借助树形DP中较为常用的思路:换根。即先算出以某一个点为根的哈希信息,然后再进行一次自顶向下的搜索,尝试用以父亲为根的哈希信息算出以当前节点为根的哈希信息。事实上是可行的,详见代码。于是我们就可以做到O(nlogn)的复杂度了。
那么对于这一题,我们只需对B树的每个叶子节点,看以它为根时,它的儿子传递给它的哈希值,就是去掉这个叶子后树以这个儿子为根的哈希值,于是我们就解决了这一题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const ll P=19260817;
int n,first[100010],tot,siz[100010],deg[100010];
ll pwr[100010],down[100010],Hash[100010];
vector<ll> son[100010],pre[100010];
map<ll,bool> vis;
struct edge
{
    int v,next;
}e[200010];

void insert(int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void dfs1(int v,int fa)
{
    siz[v]=1;
    for(int i=first[v];i;i=e[i].next)
        if (e[i].v!=fa)
        {
            dfs1(e[i].v,v);
            son[v].push_back(down[e[i].v]);
            siz[v]+=siz[e[i].v];
        }
    sort(son[v].begin(),son[v].end());
    down[v]=0;
    for(int i=0;i<son[v].size();i++)
    {
        down[v]=down[v]*P+son[v][i];
        pre[v].push_back(down[v]);
    }
    down[v]=down[v]*P+(ll)siz[v];
}

void dfs2(int v,int fa)
{
    deg[v]=1;
    if (fa)
    {
        int s=son[fa].size(),l=0,r=s-1;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if (down[v]>son[fa][mid]) l=mid+1;
            else r=mid;
        }
        ll up=0;
        if (l>0) up=pre[fa][l-1]*pwr[s-l-1];
        up+=pre[fa][s-1]-pre[fa][l]*pwr[s-l-1];
        up=up*P+(ll)(n-siz[v]);

        son[v].push_back(up);
        sort(son[v].begin(),son[v].end());
        Hash[v]=0;
        pre[v].clear();
        for(int i=0;i<son[v].size();i++)
        {
            Hash[v]=Hash[v]*P+son[v][i];
            pre[v].push_back(Hash[v]);
        }
        Hash[v]=Hash[v]*P+(ll)n;
    }
    else Hash[v]=down[v],deg[v]=0;
    for(int i=first[v];i;i=e[i].next)
        if (e[i].v!=fa) deg[v]++,dfs2(e[i].v,v);
}

int main()
{
    scanf("%d",&n);
    pwr[0]=1;
    for(int i=1;i<=n;i++)
        pwr[i]=pwr[i-1]*P;

    memset(first,0,sizeof(first));
    tot=0;
    for(int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        insert(a,b),insert(b,a);
    }

    dfs1(1,0);
    dfs2(1,0);
    for(int i=1;i<=n;i++)
        vis[Hash[i]]=1;

    memset(first,0,sizeof(first));
    tot=0;
    n=n+1;
    for(int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        insert(a,b),insert(b,a);
        son[i].clear();
        pre[i].clear();
    }

    dfs1(1,0);
    dfs2(1,0);
    for(int i=1;i<=n;i++)
        if (deg[i]==1&&vis[son[i][0]])
        {
            printf("%d",i);
            break;
        }

    return 0;
}
posted @ 2018-07-05 19:28  Maxwei_wzj  阅读(164)  评论(0编辑  收藏  举报