【題解】codeforces778C Peterson Polyglot

題目鏈接

題意:給定一棵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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章