树数树
传送门
做法:
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();
}
}