树数树

传送门

做法:

dp

误区:不能直接只用 \(i\) 最大的两个儿子来更新 \(f[i]\),因为深度更大的儿孙中可能有更有的选择。

70分:每个点开一个堆,每次将未被选到的节点和自己的信息传递给父亲,对于 \(f[i]\),将子节点的节点信息索要到当前节点(将儿子的堆合并到此节点的堆上),取出最大的两个将其删除累加到自身,再将自己的值放进堆中传递给父亲。

100分:启发式合并,每次将更小的堆合并到大堆上。

code:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+8;
int f[N];
int fr[N],to[N<<1],nxt[N<<1],too=0;
int n;
priority_queue <int> q[N];
int rt[N]; 
void add(int x,int y)
{
	to[++too]=y;
	nxt[too]=fr[x];
	fr[x]=too;
}
int mg(int x,int y)
{
	if(q[x].size()<q[y].size()) swap(x,y);
	while(!q[y].empty()){
		q[x].push(q[y].top());
		q[y].pop();
	}
	return x;
}
void dfs(int x,int fa)
{
	rt[x]=x;
	for(int i=fr[x];i;i=nxt[i])
	{
		int y=to[i];
		if(y==fa) continue;
		dfs(y,x);
		rt[x]=mg(rt[x],rt[y]);
	}
	f[x]=1;
	if(!q[rt[x]].empty()) f[x]+=q[rt[x]].top(),q[rt[x]].pop();
	if(!q[rt[x]].empty()) f[x]+=q[rt[x]].top(),q[rt[x]].pop();
	q[rt[x]].push(f[x]);
	return ; 
}
int main()
{
	freopen("C.in","r",stdin);
	freopen("C.out","w",stdout);
	int T;
	cin>>T;
	while(T--)
	{
		too=0;
		memset(fr,0,sizeof(fr));
		scanf("%d",&n);
		int x,y;
		for(int i=1;i<n;i++)
		{
			scanf("%d%d",&x,&y);
			add(x,y);add(y,x);
		}	
		dfs(1,0);
		printf("%d\n",f[1]);
		while(!q[rt[1]].empty()) q[rt[1]].pop();
	}
}
posted @ 2021-10-27 20:26  ☄️ezuyz☄️  阅读(40)  评论(0编辑  收藏  举报