关于树上差分的一些理解

借鉴博客

一些题目: (由易到难)
洛谷P3258松鼠的新家
洛谷P2680运输计划
洛谷P1084疫情控制

树上差分是一个很好的技巧. 主要分为下面两种:

一.关于边的差分(如找被所有路径共同覆盖的边)

首先我们除了一般的up,deep等数组以外,多开一个数组:sum。

sum用来记录点的出现次数(具体点说实际上记录的是点到其父亲的边的出现次数, 我们这样处理:

sum[u]++,sum[v]++,sum[LCA(u, v)]-=2。(记住:最后要从所有叶结点把权值向上累加)以一次操作为例,我们来看看效果(可以画一张图)。首先sum[u]++,一直推上去到根,这时候u到root的路径访问次数都+1,tmp[v]++后,v到lca路径加了1,u到lca路径加了1,而lca到根的路径加了2。

这时,我们只需要sum[LCA(u, v)]-=2,推到根,就能把那些多余的路径减掉,达到想要的目的。而这是一次操作,对于很多次操作的话,我们只需要维护sum,而不必每次更新到根,维护好sum最后dfs一遍即可。这时如果sum[i]==次数的话,说明 i 到其父亲的边是被所有路径覆盖的。如图
这里写图片描述

例题: 运输控制
首先如果我们知道了一个时间, 我们就可以找到那些点对之间不可能在这个时间完成或者可能, 所以我们考虑二分答案。 怎么check了?对于不满足条件的路径, 记一个数量cnt, 还要记录其中最大的路径值mx, 我们要做的就是找一条边它被覆盖了>=cnt次, 并且该条边权是>=mx-mid的, 如果存在那么这个mid就是答案的一种可能. 所以这里涉及了边的覆盖, 就要用上面所讲的知识

AC Code

const int maxn = 3e5 + 5;
int up[maxn][21], sum[maxn];
int deep[maxn], dis[maxn], dd[maxn];
int cnt, head[maxn];
int n, m;
struct node {
    int to, next, w;
}e[maxn<<1];
void init() {
    Fill(head,-1); cnt = 0; Fill(sum, 0);
    Fill(up,0); Fill(deep,0); Fill(dis, 0);
}
void add(int u, int v, int w) {
    e[cnt] = node{v, head[u], w};
    head[u] = cnt++;
}
void dfs(int u,int fa,int d) {
    deep[u] = d + 1;
    for(int i = 1 ; i < 20 ; i ++) {
        up[u][i] = up[up[u][i-1]][i-1];
    }
    for(int i = head[u] ; ~i ; i = e[i].next) {
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值