4474: [Jsoi2015]isomorphism
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 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
4
1 4
2 4
3 4
5
1 3
2 3
3 4
4 5
Sample Output
1
4
4
HINT
Source
无根树的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;
}