zoukankan      html  css  js  c++  java
  • 【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");
    }
  • 相关阅读:
    深入了解JVMzz
    正则表达式和Java编程语言1zz
    全世界所有程序员都会犯的错误zz
    C++完美实现Singleton模式zz
    Visual C++6.0 API函数操作技巧集zz光标和鼠标操作
    用next_permutation()生成r组合数,兼VC7的一个bugzz
    基于逆向最大化词表中文分词法zz
    c#.net常用函数列表
    Windows多线程多任务设计初步zz
    在Linux中实现内部进程通信
  • 原文地址:https://www.cnblogs.com/firecrazy/p/11656050.html
Copyright © 2011-2022 走看看