介于这道题没大佬发重心的写法,我就来凑个热闹
-
前置知识
一.树的重心
定义如下:删掉某节点\(i\)后,若剩余\(k\)个连通分量,那么定义\(d(i)\)为这些连通分量中节点数的最大值。所谓重心,就是使得\(d(i)\)最小的节点\(i\)。
定理:重心最多有两个
证明:比较感性的理解:一个重心代表一种最优均分的方案,最坏的情况就是左右为难,两个划分同样优
求法:
//size[x]为x子树大小
//maxl为最后min{d(i)}
inline void DFS(re int x,re int fa){
size[x]=1;
re int i,y,res=0;
for(i=h[x];i;i=e[i].next){
y=e[i].to;if(y==fa)continue;
DFS(y,x);
size[x]+=size[y];
res=max(res,size[y]);
}
res=max(res,n-size[x]);d[x]=res;
maxl=min(maxl,res);
}
最后比较一下重心即可出来
//每次调用时
maxl=INF;DFS(1,0);
for(j=1;j<=n;++j){if(d[j]==maxl)rt[++tot]=j;}
二.树的同构
- 引入:\(POJ1635\)
- 概念:我们对一棵树的最小\(01\)欧拉序(\(0->\)入 \(1->\)出)称为其最小表示,判断两树是否同构可比较它们的最小表示
- 实现:
- 法一:根据定义直接递归解决
- 法二:对于更大的数据我们采用树\(Hash\)
核心:
inline int Solve(re int x,re int fa){ re int i,y,res=2333; re vector<int > t; for(i=h[x];i;i=e[i].next){ y=e[i].to;if(y==fa)continue; t.push_back(Solve(y,x)); } sort(t.begin(),t.end()); for(i=0;i<t.size();++i)res=((res*Mul)^t[i])%Mod; return res; }
对这道题:考虑对两棵同构的无根树,我们只需要比较其以树上固定点(即不会因为编号方式改变改变的点)为根的\(Hash\)值,重心则是一个很好的例子
步骤:先找每棵树重心(最多两个),以重心为根来比较
注意一些细节:
由于重心的编号顺序不一定因此我们每次都要存下所有重心为根\(Hash\)值
还是放一下代码:
inline void DFS(re int x,re int fa){
size[x]=1;
re int i,y,res=0;
for(i=h[x];i;i=e[i].next){
y=e[i].to;if(y==fa)continue;
DFS(y,x);
size[x]+=size[y];
res=max(res,size[y]);
}
res=max(res,n-size[x]);d[x]=res;
maxl=min(maxl,res);
}
inline int Solve(re int x,re int fa){
re int i,y,res=2333;
re vector<int > t;
for(i=h[x];i;i=e[i].next){
y=e[i].to;if(y==fa)continue;
t.push_back(Solve(y,x));
}
sort(t.begin(),t.end());
for(i=0;i<t.size();++i)res=((res*Mul)^t[i])%Mod;
return res;
}
int main(void){
re int i,j,x;
scanf("%d",&m);
memset(ans,INF,sizeof ans);
for(i=1;i<=m;++i){
scanf("%d",&n);
cnt=0;memset(h,0,sizeof h);tot=0;
for(j=1;j<=n;++j){scanf("%d",&x);if(x){AddEdge(j,x);AddEdge(x,j);}}
maxl=INF;DFS(1,0);
for(j=1;j<=n;++j){if(d[j]==maxl)rt[++tot]=j;}
for(j=1;j<=tot;++j)ans[i]=min(ans[i],Solve(rt[j],0));
for(j=1;j<=i;++j)if(ans[j]==ans[i]){printf("%d\n",j);break;}
}
return 0;
}
原文地址:https://www.cnblogs.com/66t6/p/11622928.html
时间: 2024-11-14 12:16:08