CSU-1837 Tree Count(树的同构)

来源:互联网 发布:大数据 nosql 编辑:程序博客网 时间:2024/06/05 17:21

1837: Tree Count

        Time Limit: 5 Sec     Memory Limit: 256 Mb     Submitted: 61     Solved: 18    

Description

A tree T = (V, E), where V = {v1, v2,…, vn} is a set of vertices and E = {e1, e2,…, en-1} is a set of edges, is a connected graph with no cycle. A labeled tree T = (V, E) is a tree, where each of its vertices has an associated unique number. More precisely, there is a one-to-one function mT : V → {…, -2, -1, 0, 1, 2,…}. 
Let dT(u,v) = mT(u) - mT(v), where u, v ∈V. Two labeled trees T1 = (V1, E1) and T2 = (V2, E2) are similar if and only if 

  1. |V1| = |V2|, and 
  2. There is a bijection f: V1→V2 such that (u,v)∈E1 if and only if (f(u),f(v))∈E2. In other words, the trees are isomorphic. 
  3. Moreover,dT1(u,v)=dT2(f(u),f(v))for every (u,v)∈E1
    Figure 1: Trees T1 and T2 are similar
    In Figure 1,trees T1 and T2 are similar according to the definition. The bijection is as drawn in the figure. Furthermore, it can be verified, for instance, that dT1(v1,v2)= 1 - 9 = -8 = 9 - 17 =dT2(v4, v5) and dT1(v2, v4) = 9 - 6 = 3 = 17 - 14 =dT2(v5, v1). Also,dT1(v2, v3) = 9 - 7 = 2 = 17 - 15 =dT2(v5, v3). 
    Given d labeled trees, your job is to count similar trees. 

Input

The first line of the input file contains the number of labeled trees d ≤ 100. 2 ≤ n ≤ 70,000. The following lines describe each tree and its labels. Each tree uses two lines to describe. The first line gives all edges of the tree. Tree nodes are numbered 1, 2, 3, …, n. The second line gives the n labels of the tree in the order of vertices v1, v2, v3, v4,…, vn. -100,000< m(vi)< 100,000

Output

There is only one line in the output file. This line contains the numbers of similar trees in the increasing order. Note that the sum of all numbers in this line is d.

Sample Input

71 2 2 36 18 91 2 2 3 2 4 3 5 3 61 9 7 6 15 303 2 3 6 1 5 4 5 3 514 23 15 9 17 382 1 1 315 3 63 2 3 6 1 5 4 5 3 5 5 714 23 15 9 17 38 112 3 3 17 4 161 2 2 3 2 4 3 5 3 61 9 7 10 15 13

Sample Output

1 1 2 3

题意:有D颗树,每个树的节点不超过n个(n<=70000),每个节点都有权值val[u],边的权值定义为d(u,v)=val[u]-val[v]。

对于树的同构有以下定义:①节点数相同;②第一颗树上的每条边,在第二颗树上都有对应的相同边,即d1(u,v)=d2(f(u),f(v))
现在将相互同构的树分为一组,问每一组的树的数量,要求从小到大排序

题解:
首先明确一下树的大小判断标准:
①根节点度数大的树大
②根节点度数一样时,将子树排序,然后依次判断每个子树,第一个子树大的树大

对于有根树的同构有2种解法:hash法和最小表示法
我这里用到hash法:对树的权值进行hash,父节点的hash[u]等于自己的权值与所有子节点的hash[v]相乘,根节点的hash[u]相等则2棵树同构。

对于无根树,只要确定一个根,就转换成有根树同构的判断了。
将树看成无向图,进行拓扑排序(每次删除度为1的点),最后剩余1或2个点。
如果是1个点,以该点为根建立有根树;

如果是2个点,一棵树以任一点为根建树,另一棵树分别以2个点建立树,判断两次。

#include<cstdio>#include<algorithm>#include<string.h>#include<vector>#include<queue>#include<string>#include<iostream>#include<sstream>using namespace std;typedef unsigned long long ULL;const int MX = 7e4 + 5;const int inf = 0x3f3f3f3f;const int P1 = 1000001;const int P2 = 3111111;queue<int>q;struct Edge{    int u,v,nxt;}E[MX*4];ULL H[MX];int val[MX],level[MX],head[MX],tot,IN[MX];char buff[MX*100];void init(){    while(!q.empty())q.pop();    memset(IN,0,sizeof(IN));    memset(head,-1,sizeof(head));    memset(level,0,sizeof(level));    tot=0;}void add(int u,int v){    E[tot].u=u;    E[tot].v=v;    E[tot].nxt=head[u];    head[u]=tot++;    IN[v]++;}struct Tree{    int n,root[2],rt;    ULL Hash[2];    void top_sort(){        for(int i=1;i<=n;i++) if(IN[i]==1) q.push(i);        int Max_L=0;        while(!q.empty()){            int u=q.front();q.pop();            Max_L=max(Max_L,level[u]);            for(int i=head[u];~i;i=E[i].nxt){                int v=E[i].v;                IN[v]--;                if(IN[v]==1){                    level[v]=level[u]+1;                    q.push(v);                }            }        }        rt=0;        for(int i=1;i<=n;i++) if(level[i]==Max_L) {            root[rt++]=i;        }    }    void read(){        n=1;        gets(buff);        int u=0,v=0,len=strlen(buff);        buff[len++]=' ';        for(int i=0,j=0;i<len;i++){            if(buff[i]==' ') {                if(j%2==1) {                    add(u,v);add(v,u);                    u=v=0;                    n++;                }                j++;                continue;            }            if(j%2==0) u=u*10+buff[i]-'0';            else v=v*10+buff[i]-'0';        }        for(int i=1;i<=n;i++) scanf("%d",&val[i]);        gets(buff);    }}T[105];int dfs(int u,int fa){    if(fa==-1) H[u]=P1;    else H[u]=(ULL)(val[fa]-val[u])^P1;    for(int i=head[u];~i;i=E[i].nxt){        int v=E[i].v;        if(v==fa) continue;        H[u]*=dfs(v,u)^P2;    }    return H[u];}bool cmp(int x,int y){    for(int i=0;i<T[x].rt;i++){        for(int j=0;j<T[y].rt;j++){            if(T[x].Hash[i]==T[y].Hash[j]) return 1;        }    }    return 0;}int vis[105];vector<int>v[105];int main(){    int d;    //freopen("in.txt","r",stdin);    while(~scanf("%d\n",&d)){        for(int i=0;i<d;i++) {            init();            T[i].read();            T[i].top_sort();            for(int j=0;j<T[i].rt;j++) T[i].Hash[j]=dfs(T[i].root[j],-1);        }        for(int i=0;i<d;i++) v[i].clear();        memset(vis,0,sizeof(vis));        for(int i=0;i<d;i++){            if(vis[i]) continue;            for(int j=i+1;j<d;j++){                if(vis[j]||T[i].n!=T[j].n) continue;                if(cmp(i,j)){                    vis[i]=vis[j]=1;                    v[i].push_back(j);                }            }        }        int tot=d;        priority_queue<int,vector<int>,greater<int> >Q;        for(int i=0;i<d;i++) if(v[i].size()>0){            tot-=v[i].size()+1;            Q.push(v[i].size()+1);        }        for(int i=0;i<tot;i++) Q.push(1);        printf("%d",Q.top());Q.pop();        while(!Q.empty()){            printf(" %d",Q.top());Q.pop();        }        printf("\n");    }    return 0;}


0 0