### 先看一道题
[Luogu P3128 [USACO15DEC] Max Flow P](https://www.luogu.com.cn/problem/P3128)
如果我们开一个数组,统计经过每个节点的路径数量,我们应该怎么统计?
### 朴素算法
得出路径端点u和v的最近公共祖先f后,从u到f和从v到f,一个一个给中途的点增加统计。
时间复杂度 $O(NK)$,其中 $K$ 为路径数量。
单次路径统计的时间复杂度去到了 $O(n)$,倍增法求LCA省下来的时间全浪费了。
### 树链剖分
为什么要用大炮打蚊子?
### 树上差分
我们发现,本题不要求实时查询经过某个点的路径数量,只需程序的最后计算即可。
因此,对于下面这个路径示例(左),我们可以这么统计(右):
绿色代表“计数增加”,红色代表“计数减少”。
我们把一条路径拆分为了 $4$ 个操作,但眼尖的同学们一定发现了:每一条操作,端点必为根。这样,我们就有一个性质:设点a、u为树上的任一点,点v为树根,则a在u和v的路径上,当且仅当u在a的子树中。
有了这个性质,我们就可以在输入路径时,只在4个关键点(2个端点、LCA、LCA的父亲)进行计数增加/减少操作,输入结束后使用DFS统计子树和。
关键代码:
```cpp
int sum[MAXN+5];//计数数组
void dfs(int u,int faa)//程序的最后运行,用于统计子树和
{
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==faa)
{
continue;
}
dfs(v,u);
sum[u]+=sum[v];
}
ans=max(ans,sum[u]);//得到最大的答案
}
int main()
{
//...
//输入边
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
int f=lca(u,v);
sum[u]++;
sum[v]++;
sum[f]--;
sum[fa[f]]--;
}
dfs(1,0);
//...
}
```