bzoj 5195: [Usaco2018 Feb]Directory Traversal【树形dp】

注意到目录是一颗树结构,然后就简单了,预以1为根的处理出dis[u]为以这个点为根,到子树内的目录总长,si为子树内叶子数
第二遍dfs换根即可

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100005;
int n,h[N],cnt,tot,si[N],de[N],l[N];
long long f[N],mn,dis[N];
bool v[N];
char c[20];
struct qwe
{
	int ne,to;
}e[N<<1];
int read()
{
	int r=0,f=1;
	char p=getchar();
	while(p>'9'||p<'0')
	{
		if(p=='-')
			f=-1;
		p=getchar();
	}
	while(p>='0'&&p<='9')
	{
		r=r*10+p-48;
		p=getchar();
	}
	return r*f;
}
void add(int u,int v)
{
	cnt++;
	e[cnt].ne=h[u];
	e[cnt].to=v;
	h[u]=cnt;
}
void pre(int u,int fa)
{
	if(v[u]) 
		return;
	for(int i=h[u];i;i=e[i].ne)
		if(e[i].to!=fa)
		{
			pre(e[i].to,u);
			si[u]+=si[e[i].to];
			dis[u]+=dis[e[i].to];
		}
	dis[u]+=si[u]*l[u];
}
void dfs(int u,int fa)
{
	for(int i=h[u];i;i=e[i].ne)
		if(e[i].to!=fa)
		{
			f[e[i].to]=f[u]-si[e[i].to]*l[e[i].to]+3*(tot-si[e[i].to]);
			dfs(e[i].to,u);	
			mn=min(mn,f[e[i].to]);
		}
}
int main()
{
	n=read();
	for(int i=1;i<=n;++i)
	{
		scanf("%s",c);
		int m=read();
		l[i]=strlen(c)+1;
		if(!m) 
			tot++,dis[i]=strlen(c),v[i]=si[i]=1;
		while(m--)
		{
			int x=read();
			add(i,x),add(x,i);
		}
	}
	pre(1,1);
	for(int i=h[1];i;i=e[i].ne)
		dis[1]-=l[1]*si[e[i].to];
	f[1]=dis[1];
	mn=f[1];
	dfs(1,1);
	printf("%lld",mn);
	return 0;
}
原文地址:https://www.cnblogs.com/lokiii/p/9680970.html