• 【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;
    }
  • 相关阅读:
    天气预报FLEX版本
    关于“ORA01000: 超出打开游标的最大数”
    WIN7(x64) IIS7.5 404.17错误:请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理。
    解决GDI+中“内存不足”问题
    Stack Overflow Exception
    清洁的Javascript
    设置SQL Server数据库中某些表为只读的多种方法
    程序员肿么了?为何总被认为是“屌丝”
    jquery datepicker 显示12个月份
    apache2.4配置虚拟主机随记
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793299.html
Copyright © 2020-2023  润新知