# [P4268 [USACO18FEB] Directory Traversal G](https://www.luogu.com.cn/problem/P4268)
## 题目描述:
个人感觉这道题目读着费劲,大概意思是说文件夹里有文件,问从最大的从某个文件夹到文件(叶子)的距离和(就是“../../folder3/file3”这类操作的“相对路径”长度和)
## 输入:
请自行观看代码
## 算法分析:
用类似**换根dp**的操作。由于如果使用朴素算法时复杂度有些许炸裂,我们考虑优化。注意到从某个文件夹转移到另一个文件夹的时候,他们之间有极大的关联性,换言之有如下关系:
$f[v]=(f[u]-(w[v]+1)*leaf[v]+3*(leaf[root]-leaf[v]))$
其中,$u$ 是 $v$ 的父亲, $f[i]$ 表示**某个文件夹到其子树的所有叶子节点的路径长度**, $leaf[i]$ 表示某个节点的**叶子数目**, $w[v]$ 表示某个节点与父节点之间的**相对路径长度**,即在这里, $w[i]$ 为文件(夹)名的长度。
是什么意思呢?就是说对于 $u$ 的下一个节点 $v$ ,他的 $f[i]$ 要由 $f[u]$ 减去向 $v$ 方向多走的 $u->v$ 的道路,道路有 $leaf[v]$ 条,权值为 $w[v]+1$ ,加一是指(/),并且加上向 $u$ 方向少走的 $v->u$ ,条数为非 $v$ 的叶子的叶子数,权值为**三**(../)
## 代码:
```cpp
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
const int MAXN=2e5+10;
int n,m,w[MAXN+10],root;
int head[MAXN+10],tot;
int leaf[MAXN+10],f[MAXN+10],sz[MAXN+10],dis[MAXN+10];//dis 根到x 的wi+1(/)
int ans=1e18;
struct Edge{
int to,nxt;
}edge[MAXN*20+10];
void add(int u,int v){
edge[++tot]=(Edge){v,head[u]};
head[u]=tot;
}
//二次扫描+换根DP
void dfs1(int u,int fa){
if(sz[u])leaf[u]=1,f[root]+=dis[u]-1;//处理root
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa)continue;
dis[v]=dis[u]+w[v]+1;
dfs1(v,u);
leaf[u]+=leaf[v];
}
}
void dfs2(int u,int fa){
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa||leaf[v]==1)continue;
f[v]=(long long)(f[u]-1ull*(w[v]+1)*leaf[v]+3ull*(leaf[root]-leaf[v]));
if(ans>f[v])ans=f[v];
dfs2(v,u);
}
}
signed main(){
//freopen("1.in","r",stdin);
//ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
string c;
for(int i=1;i<=n;++i){
int op;
cin>>c>>m;
if(!m)sz[i]=1;
if(c=="bessie")root=i,w[i]=0;
else w[i]=(long long)c.length();//文件名也有用
for(int j=1;j<=m;++j){
cin>>op;
add(i,op);
add(op,i);
}
}
dfs1(root,0);//cout<<f[root]<<"\n";
ans=f[root];//注意
dfs2(root,0);
//for(int i=1;i<=n;++i)cout<<leaf[i]<<" ";cout<<"\n";
cout<<ans;
return 0;
}
```