hdu6430 - Problem E. TeaTree (启发式合并)

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6430

题目大意:

    给你一棵有n个节点的树,树上的每个节点都有一个值v_i

    让你求对每个节点x,对其 子节点 i 与子节点 j ,其最近公共祖先lca( i, j ) = x 

    而该节点x的答案为 ans[X]=max(gcd(val[i],vaj[j])) , i , j 满足 lca(i,j) = x

题解:

    参考博客:https://blog.csdn.net/lifelikes/article/details/81980619

    因为时第一次接触启发式合并,所以这题我是直接分析上面这位大神的代码的,之后分析如下

    对于一个节点,对其最大的子树的节点信息都已经加入num与divs数组中,之后再将其他比较小的子树的节点信息一一加入两数组,查询出最大gcd。而在返回上层时,如果处理的不是父节点的最大子树,则在将这棵子树的节点信息都删除。

    复杂度分析是O(n^(3/2)*log(n))  其中的n^(1/2)是添加因数时的复杂度

AC代码(带注释):

#include <iostream> 
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 300010;
struct edge{
	int v,next;
	ll w;
};
//数的因子 
vector<int> fa[N];
//邻接表 
int tot;
int head[N];
edge es[N<<1];
//树的节点个数 节点的最大节点数的子树 
int sz[N],sma[N];

int num[N];	//当前数字出现的次数 
int divs[N];	//当前因子出现的次数 
vector<int> temp;	//子树节点统计的临时数组 

int n;
int val[N];	//节点数字 
int ans[N];

int tmp;

void add(int u,int v)
{
	es[tot].v = v;
	es[tot].next = head[u];
	head[u] = tot;
	tot ++;
}
void dfs1(int u,int pre)
{
	int i;
	sz[u] = 1;
	for(i=head[u];i!=-1;i=es[i].next)
	{
		int v = es[i].v;
		if(v == pre)	continue;
		dfs1(v,u);
		sz[u] += sz[v];
		if(sz[v] > sz[sma[u]])
			sma[u] = v;
	}
}
//增一个数字引起的各种变化 
void add_node(int u)
{
	int cnt,i;
	num[val[u]] ++;
	if(num[val[u]] == 1)
	{
		for(i=0;i<fa[val[u]].size();i++)
		{
			cnt = fa[val[u]][i];
			divs[cnt] ++;
		}
	}
}
//将当前节点树上的所有节点遍历出 
void add_tree(int u,int pre)
{
	int i;
	temp.push_back(u);
	for(i=head[u];i!=-1;i=es[i].next)
	{
		int v = es[i].v;
		if(v == pre)
			continue;
		add_tree(v,u);
	}
}
//减一个数字引起的各种变化 
void det_node(int u)
{
	int cnt,i;
	num[val[u]] --;
	if(num[val[u]] == 0)
	{
		for(i=0;i<fa[val[u]].size();i++)
		{
			cnt = fa[val[u]][i];
			divs[cnt] --;
		}
	}
}
//删除树 
void det_tree(int u,int pre)
{
	int i;
	det_node(u);
	for(i=head[u];i!=-1;i=es[i].next)
	{
		int v = es[i].v;
		if(v == pre)
			continue;
		det_tree(v,u);
	}
}
void dfs2(int u,int pre)
{
	//遍历过程 
	int i,j,k;
	if(sz[u] == 1)
	{
		ans[u] = -1;
		add_node(u);
		return;
	}
	for(i=head[u];i!=-1;i=es[i].next)
	{
		int v = es[i].v;
		if(v==sma[u] || v==pre)
			continue;
		dfs2(v,u);
		det_tree(v,u);
	}
	dfs2(sma[u],u);
	//到此,num与divs中只存有sma[u]的信息 
	//取出树中最大因数 tmp 
	tmp = 0;
	int cnt;
	for(i=head[u];i!=-1;i=es[i].next)
	{
		temp.clear();
		int v = es[i].v;
		if(v==sma[u] || v==pre)
			continue;
		//得出树中有那些节点 
		add_tree(v,u);
		for(j=0;j<temp.size();j++)
		{
			for(k=0;k<fa[val[temp[j]]].size();k++)
			{
				cnt = fa[val[temp[j]]][k];
				if(divs[cnt])
					tmp = max(tmp,cnt);
			}
		}
		for(j=0;j<temp.size();j++)
			add_node(temp[j]);
	}
	for(i=0;i<fa[val[u]].size();i++)
	{
		cnt = fa[val[u]][i];
		if(divs[cnt])
			tmp = max(tmp,cnt);
	}
	add_node(u);
	//即为当前节点答案 
	ans[u] = tmp;
}
int main()
{
	int i,j;
	//处理每个数的因子 
	for(i=1;i<N;i++)
	{
		for(j=1;i*j<N;j++)
			fa[i*j].push_back(i);
	}
	//构建邻接表 
	memset(head,-1,sizeof(head));
	memset(ans,-1,sizeof(ans));
	memset(num,0,sizeof(num));
	memset(divs,0,sizeof(divs));
	tot = 0;
	scanf("%d",&n);
	for(i=2;i<=n;i++)
	{
		int t;
		scanf("%d",&t);
		add(t,i);
	}
	for(i=1;i<=n;i++)
		scanf("%d",&val[i]);
	//处理树中每个节点 节点个数最大的子树是哪个 
	dfs1(1,-1);		//O(n)
	//得出答案 
	dfs2(1,-1);
	for(i=1;i<=n;i++)
		printf("%d\n",ans[i]);
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值