缘起
树上差分还是学了吧~ 人傻就要多读书~ 嘿嘿 洛谷 P3128 [USACO15DEC]Max Flow P
树上差分我学习了【2】,秒懂~ 和树剖一样,树上差分还是蛮简单的东西~
分析
FJ给他的牛棚的N(2≤N≤50,000)个隔间之间安装了N-1根管道,隔间编号从1到N。所有隔间都被管道连通了。
FJ有K(1≤K≤100,000)条运输牛奶的路线,第i条路线从隔间si运输到隔间ti。一条运输路线会给它的两个端点处的
隔间以及中间途径的所有隔间带来一个单位的运输压力,你需要计算压力最大的隔间的压力是多少。
【输入】
第一行 是n,k, 然后n-1行是树的边
然后k行每行s,t
【输出】
最大压力
【样例输入】
5 10
3 4
1 5
4 2
5 4
5 4
5 4
3 5
4 3
4 3
1 3
3 5
5 4
1 5
3 4
【样例输出】
9
回顾一下【1】,
差分是什么?
就是利用前缀和思想,将一个数组a的全部元素的信息融入到另一个数组b中去了.
为什么要差分优化?
因为一旦使用差分处理了a, 那么a上的[l, r]O(n)的区间操作就转换为了b上的 O(1)的b[l]、b[r+1]的操作. 特别适用于全部修改操作在全部询问前面的问题.
树上差分是什么?
不就是把线性数据结构(其实就是数组)上的差分处理搬到树上去了么?
差分的题面一般是给一堆 [l, r]上的区间操作,然后询问. 而数组上的区间对应到树上不就是树上两个点之间的唯一的路径么? 所以树上差分的题面一般是给一堆树上两个点之间的路径上的操作,然后询问.
就本题而言, 不就是给你n多树上路径s–t, 路径上的
和树剖一样(【3】、【4】),树上差分也分为点差分和边差分. 而通过【3】、【4】的学习我们知道,点树剖其实关键就是点权,而边树剖的关键就是每个点通往它的父节点的树边的权. 所以搬到树上差分也是一样的.
下面讲解
点差分
点差分最典型的题面是(其实就是本题)
给你一棵树, 然后给你n多树上路径, 最后问你树上每个点被这些路径覆盖的次数?
对每一条树上路径u–v,令a = lca(u, v) ((lca不懂的童鞋参见【5】))如果对每条路径都去先求lca(u), 然后暴力统计u到a、v到a上的点被覆盖的次数, 那么妥妥被T飞啊~
等等~ 上面这种暴力操作不就类似于数组区间[l, r]全部加上1么? 我们怎么优化的? 差分啊~
令 b[i] 是一个0数组(i是顶点). 然后对于树上路径u–v, 进行下面的操作
++b[u];
++b[v];
--b[a];
--b[fa[a]];
注意,对于每条树上路径u–v, 我们的操作都是O(1)的. 经过这些预处理之后,最后统计每个点被覆盖的次数只需要进行简单的dfs即可~
void dfs(int cur)
{
for(int h = head[cur], to; ~h; h = g[h].nxt)
{
to = g[h].to;
dfs(to);
b[cur] += b[to];
}
}
// 调用方法
dfs(root);
最后b[i]就是顶点i被覆盖的次数。
为什么上面的算法是正确的?
因为u–v 只会让u–a、a–v 这两条链上的节点被覆盖次数+1,要杜绝自fa[a]往上被覆盖的次数. 所以b[u]++保证了u–a每个节点被覆盖的次数+1(这一点你去观照上面的dfs算法), 而b[v]++ 保证了 v–a 每个节点被覆盖的次数+1,但是这样a被覆盖的次数就+2了~ 所以才要 b[a]–,这样最后a被覆盖的次数就仅仅+1,这正是我们想要的效果 那么怎么阻止fa[a]往上的节点被覆盖的次数有所新增呢? 显然就只需要 b[fa[a]]-- 即可(因为b[a]最后的效果是自增1, 所以只需要阻止这个1往上传播即可, 所以只需要让b[fa[a]]–).
边差分
理解了点差分,边差分就很好理解了. 边差分的题面 一般是
给你一棵树, 然后给你n多树上路径, 最后问你树上每条边被这些路径覆盖的次数?
令 b[i]刻画的是顶点i和它的父节点的连边被覆盖的次数,那么对于树上路径 u–v, 我们只需要做的事情是
++b[u];
++b[v];
b[a]-=2; // 阻止b[u]、b[v]自增的1往自a--fa[a]以上的边传播
然后最后要统计每条树边被覆盖的次数只需要简单的执行下面的dfs即可
void dfs(int cur)
{
for(int h = head[cur], to; ~h; h = g[h].nxt)
{
to = g[h].to;
dfs(to);
b[cur] += b[to];
}
}
// 调用方法
dfs(root);
至于原因,就不必多说了吧~
至此,已经将树上差分讲解完毕了,四不四很简单? 嘿嘿~ 下面切代码
//#include "stdafx.h"
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
//#define LOCAL
const int maxn = 5e4+5, maxl = 17;
int n,m, cnt, head[maxn], fa[maxn][maxl], dep[maxn], b[maxn], ans;
bool v[maxn];
struct Arc
{
int from, to, nxt;
}g[maxn << 1];
void addarc(int from, int to)
{
g[cnt].from = from, g[cnt].to = to, g[cnt].nxt = head[from];
head[from] = cnt++;
}
void dfs(int cur)
{
v[cur] = true;
for (int h = head[cur], to; ~h; h = g[h].nxt)
{
to = g[h].to;
if (v[to])
{
continue;
}
fa[to][0] =cur;
dep[to] = dep[cur] + 1;
dfs(to);
}
}
int lca(int x, int y)
{
if (dep[x] > dep[y])
{
swap(x, y);
}
for (int j = maxl - 1; ~j; j--)
{
if (dep[fa[y][j]] >= dep[x])
{
y = fa[y][j];
}
}
if (x == y)
{
return x;
}
for (int j = maxl - 1; ~j; j--)
{
if (fa[x][j] ^ fa[y][j])
{
x = fa[x][j];
y = fa[y][j];
}
}
return fa[x][0];
}
inline void mmax(int &a, int b)
{
if (a < b)
{
a = b;
}
}
void dfs1(int cur)
{
v[cur] = true;
for (int h = head[cur], to; ~h; h = g[h].nxt)
{
to = g[h].to;
if (v[to])
{
continue;
}
v[to] = true;
dfs1(to);
b[cur] += b[to];
}
mmax(ans, b[cur]);
}
int main()
{
#ifdef LOCAL
freopen("d:\\data.in","r",stdin);
// freopen("d:\\my.out", "w", stdout);
#endif
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
int x, y;
for (int i = 1; i< n; i++)
{
scanf("%d%d", &x, &y);
addarc(x, y);
addarc(y, x);
}
dep[1] = 1;
dfs(1);
for (int j = 1, pa; j < maxl; j++)
{
for (int i = 1; i<=n; i++)
{
pa = fa[i][j - 1];
fa[i][j] = fa[pa][j - 1];
}
}
while(m--)
{
scanf("%d%d", &x, &y);
int a = lca(x, y);
++b[x];
++b[y];
--b[a];
--b[fa[a][0]];
}
memset(v, 0, sizeof(v));
dfs1(1);
printf("%d", ans);
return 0;
}
ac情况
所属题目
P3128 [USACO15DEC]Max Flow P
评测状态
Accepted
评测分数
100
编程语言
C++
代码长度
1.87KB
用时
733ms
内存
7.50MB