【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?'
':' ');
                return 0;
            }
        }
    }
    puts("NO");
}
原文地址:https://www.cnblogs.com/firecrazy/p/11656050.html