4474: [Jsoi2015]isomorphism

4474: [Jsoi2015]isomorphism

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 19   Solved: 12
[ Submit][ Status][ Discuss]

Description

一个无向树的度数为 2的结点称为假结点,其它结点称为真结点。一个无向树的简化树
其结点由原树的全体真结点组成,两个真结点之间有边当且仅当它们在原树中有边,或者在
原树中有一条联结这两个结点的路,其中间节点全是假结点。两个无向树各自的简化树如果
同构,即存在结点之间的一一对应,使得在一个树中的任意两个结点之间有边当且仅当它们
的对应结点在另一个树中有边,则称原来的两个无向树实质同构。给定若干个无向树,将相
互实质同构的无向树只保留一个其余删除。统计剩下的相互不实质同构的无向树个数,并将
它们的简化树结点个数从小到大输出。

Input

第一行只有一个正整数 m,后面依次输入m个无向树,每个无向树先用一行输入结点个
数n,结点就用1到n表示,然后用n-1行输入n-1条无向边,每行有两个 1到n 之间的不
同的正整数,用一个空格隔开,代表这两个结点之间有无向边。两个树之间无空行。 
2<=m<=20, 2<=n<=10000

Output

第一行输出一个正整数,即输入中不计实质同构包含无向树的个数 m0(1<=m0<=m)。第
二行包含不严格递增的 m0个正整数,表示这m0个无向树的简化树结点个数。相邻两数用一
个空格隔开。

Sample Input

2
4
1 4
2 4
3 4
5
1 3
2 3
3 4
4 5

Sample Output

1
4

HINT

Source

[ Submit][ Status][ Discuss]




无根树的hash

论文传送门

这里面讲得挺全的

写的时候初始化不够GG

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 1E4 + 10;
typedef unsigned long long UL;
const UL p = 5358737;

struct data{
	UL key,siz;
	int pos;
	data(){}
	data(UL key,UL siz,int pos): key(key),siz(siz),pos(pos){}
	bool operator < (const data &B) const {
		if (key < B.key) return 1;
		if (key > B.key) return 0;
		if (siz < B.siz) return 1;
		return 0;
	}
}Now[maxn];

UL Hash[22][maxn],mi[maxn],hf[maxn],sizf[maxn]
	,N,g[maxn],f[maxn],suf[maxn],siz[maxn],Size[maxn];
int n,m,T,du[maxn],Node[22],Ans[22],fa[maxn],son[maxn];
bool Mark[maxn];

vector <int> v[maxn];

int getint()
{
	char ch = getchar();
	int ret = 0;
	while (ch < '0' || '9' < ch) ch = getchar();
	while ('0' <= ch && ch <= '9')
		ret = ret*10 + ch - '0',ch = getchar();
	return ret;
}

bool cmp1(const int &A,const int &B)
{
	if (f[A] < f[B]) return 1;
	if (f[A] > f[B]) return 0;
	if (Size[A] < Size[B]) return 1;
	return 0;
}

void Dfs1(int x,int from)
{
	for (int i = 0; i < v[x].size(); i++) {
		int to = v[x][i];
		if (to == from) continue;
		fa[to] = x;
		Dfs1(to,x);
	}
}

void Dfs2(int x)
{
	Size[x] = 1;
	f[x] = g[x] = 0;
	Mark[x] = 1;
	int tot = v[x].size();
	for (int i = 0; i < tot; i++) {
		int to = v[x][i];
		Dfs2(to);
		Size[x] += Size[to];
	}
	
	for (int i = 0; i < tot; i++)
		son[i] = v[x][i];
	sort(son,son + tot,cmp1);
	int sum = 0;
	for (int i = tot - 1; i >= 0; i--) {
		f[x] += f[son[i]]*mi[sum];
		sum += Size[son[i]];
	}
	f[x] += Size[x]*mi[sum];
}

void Dfs3(int x)
{
	int tot = 0;
	if (fa[x])
		Now[++tot] = data(hf[x],sizf[x],-1);
	for (int i = 0; i < v[x].size(); i++) {
		int to = v[x][i];
		Now[++tot] = data(f[to],Size[to],to);
	} 
	sort(Now + 1,Now + tot + 1);
	int sum = 0;
	suf[tot + 1] = siz[tot + 1] = 0;
	for (int i = tot; i; i--) {
		UL Add = Now[i].key*mi[sum];
		g[x] += Add;
		suf[i] = Add + suf[i+1];
		sum += Now[i].siz;
		siz[i] = sum;
	}
	g[x] += N*mi[N - 1];
	UL Pre = 0;
	for (int i = 1; i <= tot; i++) {
		if (Now[i].pos != -1) {
			int Nex = Now[i].pos;
			hf[Nex] = Pre*mi[siz[i + 1]] + suf[i + 1];
			sizf[Nex] = N - Size[Nex];
			hf[Nex] += sizf[Nex]*mi[sizf[Nex] - 1];
		}
		Pre *= mi[Now[i].siz];
		Pre += Now[i].key;
	}
	for (int i = 0; i < v[x].size(); i++)
		Dfs3(v[x][i]);
}

void Build_tree(int k)
{
	n = getint();
	for (int i = 1; i < n; i++) {
		int x,y;
		x = getint();
		y = getint();
		v[x].push_back(y);
		v[y].push_back(x);
		++du[x]; ++du[y];
	}
	int Root;
	for (int i = 1; i <= n; i++)
		if (du[i] != 2) {
			Root = i;
			break;
		}
	fa[Root] = 0; Dfs1(Root,0);
	for (int i = 1; i <= n; i++) v[i].clear();
	for (int i = 1; i <= n; i++)
		if (du[i] != 2 && i != Root) {
			int Fa = fa[i];
			while (du[Fa] == 2) Fa = fa[Fa];
			v[Fa].push_back(i);
		}
	Dfs2(Root);
	N = Ans[k] = Size[Root];
	Dfs3(Root);
	int tot = 0;
	for (int i = 1; i <= n; i++)
		if (Mark[i])
			Hash[k][++tot] = g[i];
	sort(Hash[k] + 1,Hash[k] + Ans[k] + 1);
	for (int i = 1; i <= n; i++) v[i].clear();
	for (int i = 1; i <= n; i++) du[i] = Mark[i] = 0;
}

int getfa(int x) {return x == fa[x]?x:fa[x] = getfa(fa[x]);}
bool Equal(int x,int y)
{
	if (Ans[x] != Ans[y]) return 0;
	for (int i = 1; i <= Ans[x]; i++)
		if(Hash[x][i] != Hash[y][i])
			return 0;
	return 1;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	mi[0] = 1;
	for (int i = 1; i < maxn; i++) 
		mi[i] = mi[i-1]*p;
	cin >> T;
	for (int i = 1; i <= T; i++)
		Build_tree(i);
	for (int i = 1; i <= T; i++) fa[i] = i;
	for (int i = 1; i < T; i++)
		for (int j = i + 1; j <= T; j++)
			if (Equal(i,j)) {
				int fi = getfa(i);
				int fj = getfa(j);
				if (fi != fj) fa[fi] = fj;
			}
	
	int ans = 0;
	for (int i = 1; i <= T; i++)
		if (i == fa[i]) Node[++ans] = Ans[i];
	sort(Node + 1,Node + ans + 1);
	printf("%d\n",ans);
	for (int i = 1; i < ans; i++)
		printf("%d ",Node[i]);
	cout << Node[ans]; 
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值