https://www.luogu.org/problemnew/show/P4268

题意:选一个文件夹为当前目录,求到所有文件的需要打的字符数之和最小的那个当前目录。

思路:看成一棵树,文件夹当中间结点,文件当成叶子,求的就是:选一个中间结点,使其到所有叶子的距离和最小。

先求根节点的总路径长度,同时把各结点下叶子总个数求出来,再依次由父节点的总路径长度推出子结点的总路径长度

对于一个结点u,u下面的叶子路径长度全部减去u的长度+1(/),其他不在u下面的叶子结点全部加上3(../)。

注意要开long long。

#include<bits/stdc++.h>
using namespace std;
#define maxn 100000+100

long long n,len[maxn],file[maxn],ans;
vector<long long> son[maxn];

long long dfs(long long u,long long num)
{
	long long ans=0;
	if(son[u].size()==0)return num+len[u];
	for(long long i=0;i<son[u].size();i++)ans+=dfs(son[u][i],num+len[u]+1);
	return ans;
}
long long dfs2(long long u)
{
	if(son[u].size()==0)return 1;
	for(long long i=0;i<son[u].size();i++)file[u]+=dfs2(son[u][i]);
	return file[u];
}
void dfs3(long long u,long long fa_ans)
{
	long long _ans;	
	if(son[u].size()!=0)_ans=fa_ans-file[u]*(len[u]+1)+3*(file[1]-file[u]),ans=min(ans,_ans);
	for(long long i=0;i<son[u].size();i++)dfs3(son[u][i],_ans);
}

int main()
{
//	freopen("input.in","r",stdin);
	char str[1000];long long a,b;
	cin>>n;
	for(long long i=1;i<=n;i++)
	{
		scanf("%s%lld",str,&a);
		len[i]=strlen(str);
		while(a--)
		{
			scanf("%lld",&b);
			son[i].push_back(b);
		}
	}
	dfs2(1);
	long long root=dfs(1,-(len[1]+1));
	ans=root;	
	for(long long i=0;i<son[1].size();i++)dfs3(son[1][i],root);
	cout<<ans<<endl;
	return 0;
}