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

Description

nodgd家里种了一棵树,有一天nodgd比较无聊,就把这棵树画在了一张纸上。另一天nodgd更无聊,就又画了一张。
这时nodgd发现,两次画的顺序是不一样的,这就导致了原本的某一个节点u0在第一幅图中编号为u1,在第二副图中编号为u2。
于是,nodgd决定检查一下他画出的两棵树到底是不是一样的。nodgd已经给每棵树的节点都从1到n进行了编号,即每棵树有n个节点。
如果存在一个1到n的排列p1p2…pn,对于第一幅图中的任意一条边(u,v),在第二幅图中都能找到一条边(pu,pv),则认为这两幅图中的树是一样的。

Input

第一行一个整数n,表示节点的总数。
接下来n−1行,每行两个整数,表示第一幅图中的每一条边。
接下来n−1行,每行两个整数,表示第二幅图中的每一条边。

Output

如果两幅图的树是一样的,第一行输出”YES”,接下来1行输出一个1到n的排列p1p2…pn,两个数之间用空格间隔。当多个排列都满足题意时,你可以随便输出一个。
如果两幅图的树是不一样的,只输出一行”NO”。
注意输出的时候不要加引号。

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

Data Constraint

Data Constraint

Solution

  • 这题就直接找到树的重心,一层一层递归下去找就好了。

  • 但是要注意每个重心都要比较一遍,因为不一定是对称的!

  • 时间复杂度 O(N)

Code

#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e5+1;
int n,tot,mx,mx1,pos,pos1;
int first[N],next[N<<1],en[N<<1];
int first1[N],next1[N<<1],en1[N<<1];
int f[N],g[N],size[N],size1[N],id[N],bz[N];
bool strange;
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline void write(int x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline void insert(int x,int y)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
inline void insert1(int x,int y)
{
    next1[++tot]=first1[x];
    first1[x]=tot;
    en1[tot]=y;
}
inline void dfs(int x,int y)
{
    size[x]=1;
    for(int i=first[x];i;i=next[i])
        if(en[i]!=y)
        {
            dfs(en[i],x);
            size[x]+=size[en[i]];
            if(size[en[i]]>f[x]) f[x]=size[en[i]];
        }
    if(n-size[x]>f[x]) f[x]=n-size[x];
    if(f[x]<mx) mx=f[pos=x];
}
inline void dfs1(int x,int y)
{
    size1[x]=1;
    for(int i=first1[x];i;i=next1[i])
        if(en1[i]!=y)
        {
            dfs1(en1[i],x);
            size1[x]+=size1[en1[i]];
            if(size1[en1[i]]>g[x]) g[x]=size1[en1[i]];
        }
    if(n-size1[x]>g[x]) g[x]=n-size1[x];
    if(!strange && g[x]<mx1) mx1=g[pos1=x];
}
inline bool judge(int x,int y,int x1,int y1)
{
    if(size[x]!=size1[x1] || f[x]!=g[x1]) return false;
    for(int i=first[x];i;i=next[i])
        if(en[i]!=y)
        {
            bool pd=false;
            for(int j=first1[x1];j;j=next1[j])
                if(en1[j]!=y1 && bz[en1[j]]!=x && judge(en[i],x,en1[j],x1))
                {
                    bz[en1[j]]=x;
                    id[en[i]]=en1[j];
                    pd=true;
                    break;
                }
            if(!pd) return false;
        }
    return true;
}
int main()
{
    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        insert(x,y);
        insert(y,x);
    }
    tot=0;
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        insert1(x,y);
        insert1(y,x);
    }
    mx=mx1=1e9;
    dfs(1,0),dfs1(1,0);
    memset(f,0,sizeof(f));
    dfs(pos,mx=0);
    for(int i=1;i<=n;i++)
        if(g[i]==mx1)
        {
            strange=true;
            memset(g,0,sizeof(g));
            memset(bz,0,sizeof(bz));
            dfs1(i,0);
            if(judge(pos,0,i,0))
            {
                id[pos]=i;
                puts("YES");
                for(int i=1;i<=n;i++) write(id[i]),putchar(' ');
                return 0;
            }
        }
    puts("NO");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值