题目链接:点击查看
题目大意:给出一棵 n 个节点的树 A ,再给出一棵 n + 1 个节点的树 B,题目保证了树 B 是树 A 添加了一个叶子结点后的一棵树,只不过编号的顺序不同,现在问这个叶子节点是哪个节点,如果有多个满足条件的节点,输出编号最小的那个
题目分析:题目的意思是树 B 去掉一个叶子节点后,与树 A 同构,这样我们可以先以点 1 为根节点,用树上哈希 O( n ) 求出 dp1[ 1 ] 表示以点 1 为根节点时树的哈希值,然后利用换根 dp ,O( n ) 求出 dp2[ x ] 表示以点 x 为根节点时树的哈希值,同理处理一下树 B ,当树 B 中某个叶节点,满足:除去这个叶节点后,剩下的树 B 的任意一个节点,以其为根节点时的哈希值,在树 A 中可以找到对应的值,就说明删除掉该叶子节点后两个树是同构的,这里我们可以用 set 储存一下树 A 中 n 个节点的哈希值便于查找,随后枚举树 B 中的叶子结点进行判断即可
为了方便查找,我们可以对树 B 上的每个叶子节点,取其父节点,根据树上哈希的转移方程 dp1[ u ] += dp1[ v ] * p[ sz[ v ] ] 可知,在去掉该叶子结点后,以其父节点为根节点时的哈希值为 dp2[ u ] - p[ 1 ] ,直接查找就好了
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e6+100;
int p[N<<1];
bool vis[N<<1];
int cnt;
vector<int>node[N];
ull dp1[N],dp2[N];
set<ull>st;
int sz[N],n;
void P()//欧拉线性筛
{
vis[1]=true;
for(int i=2;i<N<<1;i++)
{
if(!vis[i])
p[cnt++]=i;
for(int j=0;j<cnt&&p[j]*i<N<<1;j++)
{
vis[p[j]*i]=true;
if(i%p[j]==0)
break;
}
}
}
void dfs1(int u,int fa)
{
dp1[u]=sz[u]=1;
for(auto v:node[u])
{
if(v==fa)
continue;
dfs1(v,u);
dp1[u]+=dp1[v]*p[sz[v]];
sz[u]+=sz[v];
}
}
void dfs2(int u,int fa)
{
dp2[u]=dp1[u];
for(auto v:node[u])
{
if(v==fa)
continue;
ull tempu=dp1[u],tempv=dp1[v];
dp1[u]-=dp1[v]*p[sz[v]];
dp1[v]+=dp1[u]*p[n-sz[v]];
dfs2(v,u);
dp1[u]=tempu;
dp1[v]=tempv;
}
}
void init(int n)
{
for(int i=1;i<=n;i++)
node[i].clear();
}
void solve()
{
init(n);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
node[u].push_back(v);
node[v].push_back(u);
}
dfs1(1,-1);
dfs2(1,-1);
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
P();
random_shuffle(p+1,p+1+100000);
scanf("%d",&n);
solve();
for(int i=1;i<=n;i++)
st.insert(dp2[i]);
n++;
solve();
for(int i=1;i<=n;i++)
if(node[i].size()==1&&st.find(dp2[node[i][0]]-p[1])!=st.end())//叶子结点
{
printf("%d\n",i);
break;
}
return 0;
}