题意:给定一棵trie树,可以删除一层边,再将父边被删且父亲相同的结点对应的子树合并得到一棵新trie树,求新trie树的最小结点数。
分析:启发式合并。在合并结点u的子树时,选择将小子树合并到大子树里,这样总的合并的时间复杂度是O(nlgn)的。
证明:合并的耗时来自于对小子树的遍历。设全体小子树的遍历总量为T,考虑每个结点u对T的贡献。设结点u可以作为小子树的第i1层、i2层、...、it层(i1<i2<...<it),则结点u对T的贡献为t。由合并的方式(小子树合并到大子树)知对应的子树tree1,tree2,...,treet有size(tree1)<size(tree2)/2<...<size(treet)/2^t。所以t不超过lgn。所以T为nlgn级别的量。
程序实现上可以先正着将结点u的小子树合并到u的最大子树里,再倒着删除,然后考虑u的子结点。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=6e5+10,maxl=26;
int n,G[maxn][maxl],sc[maxn],sz[maxn],po[maxn];
int f[maxn];
void dfs1(int u)
{
sz[u]=1;
for (int i=0;i<maxl;i++)
if (G[u][i])
{
dfs1(G[u][i]);
if (sz[G[u][i]]>sz[po[u]]) po[u]=G[u][i];
sz[u]+=sz[G[u][i]];
}
}
void Union(int u,int v,int &s)
{
s++;
for (int i=0;i<maxl;i++)
if (G[u][i]&&G[v][i])
Union(G[u][i],G[v][i],s);
else if (G[u][i]&&!G[v][i])
G[v][i]=G[u][i];
}
void Delete(int u,int v)
{
for (int i=0;i<maxl;i++)
if (G[u][i]==G[v][i])
G[v][i]=0;
else if (G[u][i]&&G[v][i])
Delete(G[u][i],G[v][i]);
}
void dfs2(int u,int h)
{
if (!sc[u]) return ;
int sum=1;
for (int i=0;i<maxl;i++)
if (G[u][i]&&G[u][i]!=po[u])
Union(G[u][i],po[u],sum);
f[h]+=sum;
for (int i=maxl-1;i>=0;i--)
if (G[u][i]&&G[u][i]!=po[u])
Delete(G[u][i],po[u]);
for (int i=0;i<maxl;i++)
if (G[u][i])
dfs2(G[u][i],h+1);
}
int idx(char c)
{
return c-'a';
}
int main()
{
cin>>n;
for (int i=1;i<n;i++)
{
int u,v;
char ch[2];
scanf("%d%d%s",&u,&v,ch);
G[u][idx(ch[0])]=v;
sc[u]++;
}
dfs1(1);
dfs2(1,1);
int ans=1;
for (int i=2;i<=n;i++)
if (f[ans]<f[i])
ans=i;
cout<<n-f[ans]<<endl<<ans;
return 0;
}