P4268 [USACO18FEB] Directory Traversal G 题解
换根 DP 基础题。
这道题相当于问你,一棵树,选一个节点使得其到所有叶子节点的路径之和最小(因为路径上必然会经过所有节点)。
我们发现从一个点换到另外一个相邻的点中间相差的部分是很容易算出来的。
让我们画个图理解一手。
如图,我们进行转移
我们需要预处理
显然时间复杂度为
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL N=1e5+5;
struct node
{
LL to,w,fw;
};
LL n,tot[N],y,cnt,f[N],sz[N],ans=1e18;
vector<node>v[N];
vector<LL>a[N];
char c[N][25];
void get(LL x,LL fa,LL len)
{
for(auto i:v[x])
{
if(i.to==fa)continue;
get(i.to,x,len+i.w);
sz[x]+=sz[i.to];
}
if(tot[x]==0)
{
f[1]+=len;
cnt++;
sz[x]++;
}
}
void dfs(LL x,LL fa)
{
ans=min(ans,f[x]);
for(auto i:v[x])
{
if(i.to==fa)continue;
f[i.to]=f[x]-sz[i.to]*i.w+(cnt-sz[i.to])*i.fw;
dfs(i.to,x);
}
}
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",c[i]);
scanf("%lld",&tot[i]);
for(int j=1;j<=tot[i];j++)
{
scanf("%lld",&y);
a[i].push_back(y);
}
}
for(int i=1;i<=n;i++)
{
for(LL y:a[i])
{
LL t=strlen(c[y])+1;
if(tot[y]==0)t--;
v[i].push_back({y,t,3});
v[y].push_back({i,3,t});
}
}
get(1,0,0);
dfs(1,0);
printf("%lld\n",ans);
}