codeforces 778C. Peterson Polyglot(trie启发式合并,dfs,好题)
题意:给出一棵 \(n\) 个结点 \(m\) 条边的 \(trie\) 树,树的深度从 \(0\) 开始,现要求删掉一层结点(删掉该层结点后下面的结点可能可以合并)使得剩下的点最少,输出最少的结点树并输出删除哪一层。
题解:这一题直接暴力枚举删除 \(i+1\) 层,然后把 \(i+2\) 层的子树进行\(trie\)启发式合并,算合并后结点数即可。
详见代码!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int inf=0x3fffffff;
const ll mod=1000000007;
const int maxn=3e5+100;
int head[maxn];
struct edge
{
int to,next,w;
}e[maxn*2]; //
int tol=0;
void add(int u,int v,int w)
{
e[++tol].to=v,e[tol].w=w,e[tol].next=head[u],head[u]=tol;
}
int ans[maxn]; //ans[i]表示删去deep[i]+1层减少的结点数目
int sz[maxn*2],deep[maxn],c[maxn*2][27];
int cnt;
int n;
int merge(int a,int b)
{
if(!a||!b) return a|b; //当a或b为空结点时返回非空结点,全为空时返回0
int rt=++cnt;
sz[rt]=1;
for(int i=0;i<26;i++)
{
c[rt][i]=merge(c[a][i],c[b][i]); //递归,这里要记录c[rt][i]结点值因为是多棵树两两合并
sz[rt]+=sz[c[rt][i]];
}
return rt;
}
void dfs(int u,int f)
{
sz[u]=1;
bool f1=false; //判断结点u是否为叶子结点
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==f) continue;
f1=true;
deep[v]=deep[u]+1;
c[u][e[i].w]=v;
dfs(v,u);
sz[u]+=sz[v];
}
ans[deep[u]]+=sz[u];
cnt=n;
int p=0;
for(int i=0;i<26;i++) p=merge(p,c[u][i]);
if(f1)
ans[deep[u]]-=sz[p]; //当不是叶子结点时可以删去下一层
else ans[deep[u]]-=1; //当是叶子结点时不能删去下一层,此时对该层贡献的答案应该为0.
}
int main()
{
scanf("%d",&n);
rep(i,1,n)
{
int u,v;
char s[10];
scanf("%d%d%s",&u,&v,s);
add(u,v,s[0]-'a');
add(v,u,s[0]-'a');
}
dfs(1,0);
int p=0;
rep(i,1,n) if(ans[i]>ans[p]) p=i;
printf("%d\n%d\n",n-ans[p],p+1);
return 0;
}