JZOJ5454. 【NOIP2017提高A组冲刺11.5】仔细的检查

题目

这里写图片描述

Sample Input

输入1:
3
1 2
2 3
1 3
3 2
输入2:
4
1 2
2 3
3 4
1 2
1 3
1 4

Sample Output

输出1:
YES
1 3 2
输出2:
NO
这里写图片描述

题解

题目有两问,
1、判断树是否同构。
2、找出对应点。

对应第一问,考虑树的特征。
考虑每一个节点的特征有什么因素决定:
1、它儿子的特征
2、以它为根的子树大小
3、它的儿子个数
4、它的深度

现在就要把这些决定某个节点的因素变为一个东西。
fi 表示这个节点的特征值,
一般只有按照相同的规则将这些决定因素融合成一个数,
那么这个数就是它的特征值了,
建议几个数合并的时候不要用加法,可以选择异或,次方相乘等不容易相同的方法,还可以乘上相应的系数。
关于模数,建议使用c++中的unsigned long long

现在判断是否同构就只需要判断这两棵树的根节点的特征值是否相同就可以了,
而求一次树的特征值的复杂度是O(n),通过换根的方法就可以快速判断两棵树是否同构,并且找出他它们同构的根节点。

之后就是要将所有点一一对应了,递归处理,
对于一个点的所有儿子,将它们的特征值排序,然后跟另一棵树对应。

本题的关键就是如何将一棵树的特征快速地表现出来。

code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define N 100003
#define tt 23333
#define w 8888888
#define ull unsigned long long
using namespace std;

int n,m,nxt1[N*2],to1[N*2],b1[N],tot1,nxt2[N*2],to2[N*2],b2[N],tot2;
int son1[N],son2[N],si1[N],si2[N],x,y,ans[N],root;
ull f1[N],f2[N];

struct node
{
    int x;
    ull y;
}p1[N],p2[N],t;
bool cmp(node a,node b)
{
    return a.y<b.y;
}

char ch;
void read(int& n)
{
    n=0;
    for(ch=getchar();ch<'0' || ch>'9';ch=getchar());
    for(;'0'<=ch && ch<='9';n=(n<<3)+(n<<1)+ch-48,ch=getchar());
}

ull F(ull x,ull y)
{
    return (x*x*tt+y*y*tt)*(w+x+y);
}

void ins1(int x,int y)
{
    nxt1[++tot1]=b1[x];
    to1[tot1]=y;
    b1[x]=tot1;
}

void ins2(int x,int y)
{
    nxt2[++tot2]=b2[x];
    to2[tot2]=y;
    b2[x]=tot2;
}

void dfs1(int x,int fa)
{
    si1[x]=1;
    for(int i=b1[x];i;i=nxt1[i])
        if(to1[i]!=fa)
        {
            dfs1(to1[i],x);
            son1[x]++;
            si1[x]+=si1[to1[i]];
            f1[x]+=f1[to1[i]];
        }
    f1[x]=f1[x]+F(son1[x],si1[x]);
}

void dfs2(int x,int fa)
{
    si2[x]=1;
    for(int i=b2[x];i;i=nxt2[i])
        if(to2[i]!=fa)
        {
            dfs2(to2[i],x);
            son2[x]++;
            si2[x]+=si2[to2[i]];
            f2[x]+=f2[to2[i]];
        }
    f2[x]=f2[x]+F(son2[x],si2[x]);
}

void exchange(int x,int y)
{
    f2[x]=f2[x]-f2[y]-F(son2[x],si2[x]);
    son2[x]--;si2[x]-=si2[y];
    f2[x]+=F(son2[x],si2[x]);

    f2[y]-=F(son2[y],si2[y]);
    son2[y]++;si2[y]=n;
    f2[y]+=f2[x]+F(son2[y],si2[y]);

}

void work(int x,int fa)
{
    if(f2[x]==f1[1])root=x;
    if(root)return;
    for(int i=b2[x];i;i=nxt2[i])
        if(to2[i]!=fa)
        {
            exchange(x,to2[i]);
            work(to2[i],x);
            if(root)return;
            exchange(to2[i],x);
        }
}

void solve(int x1,int fa1,int x2,int fa2)
{
    int ttt=0;
    for(int i=b1[x1];i;i=nxt1[i])
        if(to1[i]!=fa1)
        {
            t.x=to1[i];t.y=f1[t.x];
            p1[++ttt]=t;
        } 

    ttt=0;
    for(int i=b2[x2];i;i=nxt2[i])
        if(to2[i]!=fa2)
        {
            t.x=to2[i];t.y=f2[t.x];
            p2[++ttt]=t;
        } 

    sort(p1+1,p1+1+son1[x1],cmp);
    sort(p2+1,p2+1+son2[x2],cmp);

    for(int i=1;i<=son1[x1];i++)
        ans[p1[i].x]=p2[i].x;

    for(int i=b1[x1];i;i=nxt1[i])
        if(to1[i]!=fa1)solve(to1[i],x1,ans[to1[i]],x2);
}

int main()
{
    freopen("check.in","r",stdin);
    freopen("check.out","w",stdout);
    read(n);
    for(int i=1;i<n;i++)
        read(x),read(y),ins1(x,y),ins1(y,x);
    for(int i=1;i<n;i++)
        read(x),read(y),ins2(x,y),ins2(y,x);

    dfs1(1,0);
    dfs2(1,0);
    root=0;
    work(1,0);

    if(root==0)
    {
        printf("NO\n");
        return 0;
    }

    ans[1]=root;
    solve(1,0,root,0);

    printf("YES\n");
    for(int i=1;i<=n;i++)
        printf("%d ",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值