洛谷 - P4323 [JSOI2016]独特的树叶(树上哈希+换根dp)

题目链接:点击查看

题目大意:给出一棵 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;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108328933
今日推荐