【CSP模拟赛】仔细的检查(树的重心&树hash)
题目描述
nodgd家里种了一棵树,有一天nodgd比较无聊,就把这棵树画在了一张纸上。另一天nodgd更无聊,就又画了一张。
这时nodgd发现,两次画的顺序是不一样的,这就导致了原本的某一个节点u0在第一幅图中编号为u1,在第二副图中编号为u2。
于是,nodgd决定检查一下他画出的两棵树到底是不是一样的。nodgd已经给每棵树的节点都从1到n进行了编号,即每棵树有n个节点。
如果存在一个1到n的排列p1,p2…pn,对于第一幅图中的任意一条边(i,j),在第二幅图中都能找到一条边(pi,pj),则认为这两幅图中的树是一样的。
输入格式
第一行一个整数n,表示节点的总数。
接下来n−1行,每行两个整数,表示第一幅图中的每一条边。
接下来n−1行,每行两个整数,表示第二幅图中的每一条边。
输出格式
如果两幅图的树是一样的,第一行输出”YES”,接下来1行输出一个1到n的排列p1,p2,……,pn,两个数之间用空格间隔。当多个排列都满足题意时,你可以随便输出一个。
如果两幅图的树是不一样的,只输出一行”NO”。
注意输出的时候不要加引号。
输入样例
3
1 2
2 3
1 3
3 2
输出样例
YES
1 3 2
提示
【样例解释1】
肉眼可见,1-2-3和1-3-2显然是一样的两棵树。不过这可能不是唯一的符合题意的排列。
数据范围:n<=100000
分析
正解好像是什么括号序列,被机房的大佬用树hash强行卡过了。。。。。。
对于一棵树,什么东西与它的根节点无关呢?
反正我知道的只有重心与直径
所以把重心找出来,直接树hash,再从根节点往下一一对应,判断子节点的对应点时用hash值给子节点排个序就好了
注意不能直接根据每个节点的hash值排序对应,因为如果有两棵子树结构相同,祖先子树结构不同时它们是无法被区分的。
好像这是我第一次写树的重心
另外此题hash也有技巧,有一篇论文《Hash在信息学竞赛中的一类应用》(反正我没看,只是用了他的hash方法)
代码
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=100005;
const unsigned long long seed=998244353;
int n,cnt[2],ans[maxn];
unsigned long long ha[2][maxn];
vector<int>g[2],son[2][maxn];
int info[2][maxn],nx[2][maxn<<1],v[2][maxn<<1];
void add(int u1,int v1,int k){nx[k][++cnt[k]]=info[k][u1];info[k][u1]=cnt[k];v[k][cnt[k]]=v1;}
bool cmp0(int a,int b){return ha[0][a]<ha[0][b];}bool cmp1(int a,int b){return ha[1][a]<ha[1][b];}
int yousa(int x,int f,int k)
{
int sz=1,flag=1;
for(int i=info[k][x],del;i;i=nx[k][i])if(v[k][i]!=f)
sz+=(del=yousa(v[k][i],x,k)),flag&=((del<<1)<=n);
flag&=(((n-sz)<<1)<=n);if(flag)g[k].push_back(x);
return sz;
}
void VR(int x,int f,int k)
{
son[k][x].clear();
for(int i=info[k][x];i;i=nx[k][i])
{
if(v[k][i]!=f)
VR(v[k][i],x,k),son[k][x].push_back(v[k][i]);
}
if(k==0)sort(son[k][x].begin(),son[k][x].end(),cmp0);
if(k==1)sort(son[k][x].begin(),son[k][x].end(),cmp1);
ha[k][x]=1;
for(int i=son[k][x].size()-1;i>=0;i--)ha[k][x]=ha[k][x]*seed^ha[k][son[k][x][i]];
ha[k][x]*=seed*seed;
}
void solve(int a,int b){ans[a]=b;for(int i=son[0][a].size()-1;i>=0;i--)solve(son[0][a][i],son[1][b][i]);}
int main()
{
scanf("%d",&n);
for(int k=0;k<=1;k++)for(int i=1,u1,v1;i<n;i++)
scanf("%d%d",&u1,&v1),add(u1,v1,k),add(v1,u1,k);
yousa(1,0,0);yousa(1,0,1);
for(int i=0,lim=g[0].size();i<lim;i++)
{
VR(g[0][i],0,0);
for(int j=0,lim=g[1].size();j<lim;j++)
{
VR(g[1][j],0,1);
if(ha[0][g[0][i]]==ha[1][g[1][j]])
{
puts("YES");solve(g[0][i],g[1][j]);
for(int k=1;k<=n;k++)printf("%d%c",ans[k],k==n?'\n':' ');
return 0;
}
}
}
puts("NO");
}