zoukankan      html  css  js  c++  java
  • 树哈希学习笔记

    我们有时需要判断一些树是否同构。这时,选择恰当的Hash方式来将树映射成一个便于储存的Hash值(一般是 32 位或 64 位整数)是一个优秀的方案。

    树Hash定义在有根树上。判断无根树同构的时候,可以比较重心为根的Hash值(一个树最多有两个根)或者比较每个点为根的Hash值(后者有O(n)的求解方法)。

    本文采用的Hash方式如下:

    (f_{x}=1+sum_{y in s o n_{x}} f_{y} imes operatorname{prime}left(operatorname{size}_{y} ight))

    模板

    //dfs1:求一个点为根时所有点子树的哈希值;
    //dfs2:求每个点为根时该点的哈希值;
    struct Tree{
        struct edge{
            int v,next;
        }E[maxm];
        int head[maxn],tot;
        void addedge(int u,int v){
            E[++tot].v=v;
            E[tot].next=head[u];
            head[u]=tot;
        }
        ll hx[maxn],sz2[maxn];
        void dfs1(int u,int fa){
            sz2[u]=hx[u]=1;
            for(int i=head[u];i;i=E[i].next){
                int v=E[i].v;
                if(v==fa)continue;
                dfs1(v,u);
                hx[u]+=hx[v]*Prime[sz2[v]];
                sz2[u]+=sz2[v];
            }
        }
        ll ghx[maxn];
        void dfs2(int u,int fa,ll faf,int n){//faf:fa除去u子树,剩余树的哈希值
            ghx[u]=hx[u]+faf*(Prime[n-sz2[u]]);
            faf*=Prime[n-sz2[u]];//此时,faf=以u为根fa方向的树的哈希值-1
            for(int i=head[u];i;i=E[i].next){
                int v=E[i].v;
                if(v!=fa){
                    dfs2(v,u,faf+hx[u]-hx[v]*(Prime[sz2[v]]),n);//hx[u]-hx[v]*(Prime[size[v]):以u为根,除去fa和v方向的子树,剩余的哈希值
                }
            }
        }
    }
    

    例题

    P5043无根树同构

    题意:按顺序给出一些树,输出在这之前和该树同构的最早出现的树

    解法:找出每棵树的重心(最多两个),该树的哈希值就是以两个重心为根的哈希值的最大值(或者最小值)

    //按顺序给出一些树,输出在这之前和该树同构的最早出现的树
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=55;
    const int maxm=105;
    const int INF=1e8;
    const ll llINF=1e18;
    
    const int maxN=1e6+5;
    const int maxp=1e6+5;
    bool isPrime[maxN];
    int Prime[maxp], primecnt = 0;
    void GetPrime(int n){//筛到n
    	memset(isPrime, 1, sizeof(isPrime));
    	isPrime[1] = 0;//1不是素数
    	for(int i = 2; i <= n; i++){
    		if(isPrime[i])//没筛掉
    			Prime[++primecnt] = i; //i成为下一个素数
    		for(int j = 1; j <= primecnt && i*Prime[j] <= n; j++) {
    			isPrime[i*Prime[j]] = 0;
    			if(i % Prime[j] == 0)//i中也含有Prime[j]这个因子
    				break;
    		}
    	}
    }
    
    struct edge{
        int v,next;
    }E[maxm];
    int head[maxn],tot;
    void addedge(int u,int v){
        E[++tot].v=v;
        E[tot].next=head[u];
        head[u]=tot;
    }
    int sz[maxn],maxnum[maxn],minn;
    void dfs(int u,int fa,int n){
        sz[u]=1;
        int res=0;
        for(int i=head[u];i;i=E[i].next){
            int v=E[i].v;
            if(v==fa) continue;
            dfs(v,u,n);
            sz[u]+=sz[v];
            res=max(res,sz[v]);
        }
        res=max(res,n-sz[u]);
        maxnum[u]=res;
        minn=min(minn,maxnum[u]);
    }
    ll hx[maxn],sz2[maxn];
    void dfsh(int u,int fa){
        sz2[u]=hx[u]=1;
        for(int i=head[u];i;i=E[i].next){
            int v=E[i].v;
            if(v==fa)continue;
            dfsh(v,u);
            hx[u]+=hx[v]*Prime[sz2[v]];
            sz2[u]+=sz2[v];
        }
    }
    void getHash(int u){
        dfsh(u,0);
    }
    ll ans[maxn];
    void init(int n){
        minn=INF;
        tot=0;
        memset(head,0,sizeof(head));
    }
    int main () {
        GetPrime(maxN-5);
        int M;
        scanf("%d",&M);
        map<ll,int>vis;
        for(int i=1;i<=M;i++){
            int n;
            scanf("%d",&n);
            init(n);
            for(int u=1;u<=n;u++){
                int fa;
                scanf("%d",&fa);
                if(fa!=0){
                    addedge(u,fa);
                    addedge(fa,u);
                }
            }
            dfs(1,0,n);
            vector<int>rt;
            for(int u=1;u<=n;u++){
                if(maxnum[u]==minn){
                    rt.push_back(u);
                }
            }
            for(auto u:rt){
                getHash(u);
                ans[i]=max(ans[i],hx[u]);
            }
            if(!vis.count(ans[i])){
                vis[ans[i]]=i;
                printf("%d
    ",i);
            }
            else{
                printf("%d
    ",vis[ans[i]]);
            }
        }
    }
    

    P4323 每个点哈希

    题意:给出一棵树a,大小为n,给出另一个大小为n+1的树b,b是在a树的基础上加上一个叶子节点,并打乱节点顺序,问b树中编号最小的可能是新加入点的点。

    思路:对于a树的所有点,处理出以其为根的hash值,存在set中。同样预处理b树中所有点以其为根的hash值,对于b树中所有的和叶子节点相邻的点,若去掉相邻的叶子节点,以其为根的哈希值就会-2,判断-2后的哈希值是否在set中即可。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+5;
    const int maxm=2e5+5;
    const int INF=1e8;
    
    const int maxN=2e6+5;
    const int maxp=2e6+5;
    bool isPrime[maxN];
    int Prime[maxp], primecnt = 0;
    void GetPrime(int n){//筛到n
    	memset(isPrime, 1, sizeof(isPrime));
    	isPrime[1] = 0;//1不是素数
    	for(int i = 2; i <= n; i++){
    		if(isPrime[i])//没筛掉
    			Prime[++primecnt] = i; //i成为下一个素数
    		for(int j = 1; j <= primecnt && i*Prime[j] <= n; j++) {
    			isPrime[i*Prime[j]] = 0;
    			if(i % Prime[j] == 0)//i中也含有Prime[j]这个因子
    				break;
    		}
    	}
    }
    
    struct Tree{
        struct edge{
            int v,next;
        }E[maxm];
        int head[maxn],tot;
        void addedge(int u,int v){
            E[++tot].v=v;
            E[tot].next=head[u];
            head[u]=tot;
        }
        ll hx[maxn],sz2[maxn];
        void dfs1(int u,int fa){
            sz2[u]=hx[u]=1;
            for(int i=head[u];i;i=E[i].next){
                int v=E[i].v;
                if(v==fa)continue;
                dfs1(v,u);
                hx[u]+=hx[v]*Prime[sz2[v]];
                sz2[u]+=sz2[v];
            }
        }
        ll ghx[maxn];
        void dfs2(int u,int fa,ll faf,int n){//faf:fa除去u子树,剩余树的哈希值
            ghx[u]=hx[u]+faf*(Prime[n-sz2[u]]);
            faf*=Prime[n-sz2[u]];//此时,faf=以u为根fa方向的树的哈希值-1
            for(int i=head[u];i;i=E[i].next){
                int v=E[i].v;
                if(v!=fa){
                    dfs2(v,u,faf+hx[u]-hx[v]*(Prime[sz2[v]]),n);//hx[u]-hx[v]*(Prime[size[v]):以u为根,除去fa和v方向的子树,剩余的哈希值
                }
            }
        }
    }t1,t2;
    int du[maxn],near[maxn];
    int main () {
        GetPrime(maxN-5);
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n-1;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            t1.addedge(u,v);
            t1.addedge(v,u);
        }
        for(int i=1;i<=n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            du[u]++;du[v]++;
            near[u]=v,near[v]=u;//near存一个相邻点,对于叶子节点相邻点只有一个
            t2.addedge(u,v);
            t2.addedge(v,u);
        }
        t1.dfs1(1,0);
        t1.dfs2(1,0,0,n);
        set<int>vis;
        for(int i=1;i<=n;i++){
            vis.insert(t1.ghx[i]);
        }
        t2.dfs1(1,0);
        t2.dfs2(1,0,0,n+1);
        for(int i=1;i<=n+1;i++){
            if(du[i]!=1)continue;
            ll temp=t2.ghx[near[i]]-2;
            if(vis.count(temp)){
                printf("%d
    ",i);
                break;
            }
        }
    }
    

    参考与引用: https://www.cnblogs.com/kangkang-/p/11581726.html

  • 相关阅读:
    Smart Client Architecture and Design Guide
    Duwamish密码分析篇, Part 3
    庆贺发文100篇
    .Net Distributed Application Design Guide
    New Introduction to ASP.NET 2.0 Web Parts Framework
    SPS toplevel Site Collection Administrators and Owners
    来自Ingo Rammer先生的Email关于《Advanced .Net Remoting》
    The newsletter published by Ingo Rammer
    深度探索.Net Remoting基础架构
    信道、接收器、接收链和信道接受提供程序
  • 原文地址:https://www.cnblogs.com/ucprer/p/13490839.html
Copyright © 2011-2022 走看看