[bzoj4474][Jsoi2015]isomorphism【树哈希】
【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=4474
【题解】
先求出重心,(如果有两个重心就再新建一个点作为新重心),将无根树转化为有根树。然后再对有根树进行哈希。对于一棵子树,它的哈希值的计算方法是:
1.求出树根节点的每个儿子的子树的哈希值。
2.将这些哈希值排序。
3.每次加上一个儿子的哈希值乘以进制D + 常数 C。
4.将得到的结果开平方。
可以证明,这种构造哈希函数的方法是比较合理的。
时间复杂度 :
【代码】
/* --------------
user Vanisher
problem bzoj-4474
----------------*/
# include <bits/stdc++.h>
# define ll long long
# define inf 0x3f3f3f3f
# define N 10010
# define M 21
using namespace std;
int read(){
int tmp=0, fh=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
return tmp*fh;
}
const ll P=998244353, S=20011216, Q=19260817, B=2333333;
struct Edge{
int data,next;
}e[N*2];
struct Node{
int num; ll sum;
}fin[M];
int m, n, deg[N], head[N], place, id, nexv[N], nexu[N], p[N], num, size[N], mx[N], tmp, rt;
ll now[N], h[N];
void build(int u, int v){
e[++place].data=v; e[place].next=head[u]; head[u]=place;
}
bool cmp(Node x, Node y){
return x.sum<y.sum;
}
void del(int x, int fa, int an){
if (deg[x]!=2){
if (an!=0) nexu[++num]=x, nexv[num]=an;
for (int ed=head[x]; ed!=0; ed=e[ed].next)
if (e[ed].data!=fa) del(e[ed].data,x,x);
}
else {
for (int ed=head[x]; ed!=0; ed=e[ed].next)
if (e[ed].data!=fa) del(e[ed].data,x,an);
}
}
void heave(int x, int fa){
size[x]=1; mx[x]=0;
for (int ed=head[x]; ed!=0; ed=e[ed].next)
if (e[ed].data!=fa){
heave(e[ed].data,x);
size[x]=size[x]+size[e[ed].data];
mx[x]=max(mx[x],size[e[ed].data]);
}
mx[x]=max(mx[x],tmp-size[x]);
}
void solve(int x, int fa){
int cnt=0;
for (int ed=head[x]; ed!=0; ed=e[ed].next)
if (e[ed].data!=fa) solve(e[ed].data,x);
for (int ed=head[x]; ed!=0; ed=e[ed].next)
if (e[ed].data!=fa) now[++cnt]=h[e[ed].data];
sort(now+1,now+cnt+1);
h[x]=S;
for (int i=1; i<=cnt; i++) h[x]=(h[x]*Q+now[i]+B)%P;
h[x]=(h[x]*h[x])%P;
}
int main(){
m=read();
for (int t=1; t<=m; t++){
n=read();
memset(head,0,sizeof(head)); place=0;
memset(deg,0,sizeof(deg));
for (int i=1; i<n; i++){
int u=read(), v=read();
build(u,v), build(v,u);
deg[u]++, deg[v]++;
}
tmp=n;
for (int i=1; i<=n; i++){
if (deg[i]==1) rt=i;
if (deg[i]==2) tmp--;
}
num=0; del(rt,0,0);
memset(head,0,sizeof(head)); place=0;
for (int i=1; i<=num; i++){
build(nexu[i],nexv[i]);
build(nexv[i],nexu[i]);
}
heave(rt,0);
int mn=inf, mnsize=0;
for (int i=1; i<=n; i++){
if (deg[i]==2) continue;
if (mn>mx[i]) mn=mx[i], mnsize=0;
if (mn==mx[i]) p[++mnsize]=i;
}
if (mnsize==2){
memset(head,0,sizeof(head));
for (int i=1; i<=num; i++){
if ((nexu[i]==p[1]&&nexv[i]==p[2])||(nexu[i]==p[2]&&nexv[i]==p[1])){
build(n+1,nexu[i]), build(nexu[i],n+1);
build(n+1,nexv[i]), build(nexv[i],n+1);
}
else {
build(nexu[i],nexv[i]);
build(nexv[i],nexu[i]);
}
}
rt=n+1;
}
else rt=p[1];
solve(rt,0);
fin[t].num=tmp; fin[t].sum=h[rt];
}
sort(fin+1,fin+m+1,cmp);
int cnt=0;
for (int i=1; i<=m; i++)
if (fin[i].sum!=fin[i-1].sum)
size[++cnt]=fin[i].num;
sort(size+1,size+cnt+1);
printf("%d\n",cnt);
for (int i=1; i<=cnt; i++)
printf("%d%c",size[i],(i==cnt)?('\n'):(' '));
return 0;
}