• luogu5043


    题目描述

    树是一种很常见的数据结构。

    我们把 N 个点,N−1 条边的连通无向图称为树。

    若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树。

    对于两个树 (T_1)(T_2),如果能够把树 (T_1)的所有点重新标号,使得树 (T_1)和树 (T_2)完全相同,那么这两个树是同构的。也就是说,它们具有相同的形态。

    现在,给你 M 个无根树,请你把它们按同构关系分成若干个等价类。

    输入格式

    第一行,一个整数 M。

    接下来 M 行,每行包含若干个整数,表示一个树。第一个整数 N表示点数。接下来 N 个整数,依次表示编号为 1 到 N 的每个点的父亲结点的编号。根节点父亲结点编号为 0。

    输出格式

    输出 M 行,每行一个整数,表示与每个树同构的树的最小编号。

    输入输出样例

    输入

    4 
    4 0 1 1 2 
    4 2 0 2 3 
    4 0 1 1 1 
    4 0 1 2 3 
    

    输出

    1 
    1 
    3 
    1 
    

    说明/提示
    编号为 1, 2, 4的树是同构的。编号为 3 的树只与它自身同构。

    对于 (100\%) 的数据,(1leq N,Mleq 50)


    树的同构问题,用到树哈希,也就是把树哈希为一个数值,从而可以进行比较。
    (f[u]):以u点为根的子树的哈希值。

    [f[u]=sum_{v in son_u}f[v] imes prime[siz[v]] ]

    其中,(siz[v])表示以v为根的子树的大小,而(prime[x])表示很x个素数

    在这个题中,首先求了(f[]),但由于是无根树,所以以通过getg()函数求了(g[])表示以每一个点为根时,这棵树的哈希值。
    两棵树要同构,那么它们分别以某一点为根时,他们的哈希值一定相等。应该这样说,如果两棵树同构,那么,A树中每一个点为根,B树中必然最少有一个点为根的树的哈希值与它相同。所以我们只求出两个树中哈希值最小的值,如果他们相等,他们就是同构的,否则不可以同构。


    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=55;
    struct edge
    {
    	int u,v,nxt;
    }e[maxn<<1];
    int head[maxn],js;
    void addage(int u,int v)
    {
    	e[++js].u=u;e[js].v=v;
    	e[js].nxt=head[u];head[u]=js;
    }
    int n,m;
    bool isprime[1000];
    int prime[maxn];
    void getprime()
    {
    	int cnt=0;
    	for(int i=2;i<1000;++i)
    	{
    		if(cnt>=54)break;
    		if(isprime[i]==0)prime[++cnt]=i;
    		for(int j=1;j<=cnt;++j)
    		{
    			if(i*prime[j]>=1000)break;
    			isprime[i*prime[j]]=1;
    			if(i%prime[j]==0)break;
    		}
    	}
    }
    int f[maxn],siz[maxn];
    void dfs(int u,int fa)
    {
    	siz[u]=f[u]=1;
    	for(int i=head[u];i;i=e[i].nxt)
    	{
    		int v=e[i].v;
    		if(v==fa)continue;
    		dfs(v,u);
    		siz[u]+=siz[v];
    		f[u]+=f[v]*prime[siz[v]];
    	}
    }
    int g[maxn];
    void getg(int u,int fa,int faf)
    //faf:以u点向父亲方向(向上)的子树的f[]
    {
    	for(int i=head[u];i;i=e[i].nxt)
    	{
    		int v=e[i].v;
    		if(v==fa)continue;
    		int tp=f[u]-f[v]*prime[siz[v]]+faf*prime[n-siz[u]];
    		//tp为v点向父亲方向上的hash值
    		g[v]=f[v]+tp*prime[n-siz[v]];
    		getg(v,u,tp);
    	}
    }
    struct node{
    	int gg,no,siz;
    }sz[maxn];
    bool cmp (node a,node b)
    {
    	if(a.siz<b.siz)return 1;
    	if(a.siz==b.siz && a.gg<b.gg)return 1;
    	if(a.siz==b.siz && a.gg==b.gg && a.no<b.no)return 1;
    	return 0;
    }
    int da[maxn];
    int main()
    {
    	scanf("%d",&m);
    	getprime();
    	
    	for(int i=1;i<=m;++i)
    	{
    		scanf("%d",&n);
    		js=0;memset(head,0,sizeof head);
    		for(int v,j=1;j<=n;++j)
    		{
    			scanf("%d",&v);
    			if(v==0)continue;
    			addage(j,v);addage(v,j);
    		}
    		memset(f,0,sizeof f);
    		memset(g,0,sizeof g);
    		dfs(1,0);
    		g[1]=f[1];
    		getg(1,0,0);
    		int mn=0x7fffffff;
    		sz[i].siz=n;sz[i].no=i;
    		for(int j=1;j<=n;++j)
    		{
    			if(g[j]<mn)
    			{
    				mn=g[j];
    				sz[i].gg=g[j];
    			}
    		}
    	}
    	sort(sz+1,sz+1+m,cmp);
    	for(int i=1;i<=m;++i)
    		if(sz[i].siz==sz[i-1].siz && sz[i].gg==sz[i-1].gg)da[sz[i].no]=da[sz[i-1].no];
    		else
    			da[sz[i].no]=sz[i].no;
    	for(int i=1;i<=m;++i)printf("%d
    ",da[i]);
    	return 0;
    }
    
  • 相关阅读:
    【NOIp复习】图论算法模板合集
    【NOI导刊】【归并排序求逆序对】最接近神的人
    【NOIp 2012】【线段树】借教室
    【NOIp复习】最近公共祖先LCA&区间最大最小RMQ
    【NOIp 2015】【二分答案】跳石头
    【NOIp 2015】【DFS】斗地主
    【vijos】【BFS+hash】毒药?解药?
    【NOIp模拟】【dp】俄罗斯方块
    【NOIp模拟】【二分图or并查集】GoToandPlay
    【vjios】【DFS】切蛋糕
  • 原文地址:https://www.cnblogs.com/gryzy/p/15251547.html
Copyright © 2020-2023  润新知