定义:差分就是数组中每一项都与前一项做差,最后得出的序列。
例如:数组:2,5,3,7,2,3
差分后的序列:2,3,-2,4,-5,1,-3
注意:第一个数与原数组相同,相当于2-0;最后一个数与原数组互为相反数,相当于0-3;
差分的性质
1.差分序列的前缀和就是原数组;
2.将原数组在[L,R]区间上的数全部加1,相当于让差分数组在L处+1,在R+1处-1;

考虑差分的一个应用:


图片来源:https://blog.csdn.net/a135193...

树上差分的两种思路:

1.找被所有路径共同覆盖的边。
例题:洛谷:https://www.luogu.com.cn/prob...
分析见代码:

//1.找被所有路径共同覆盖的边。
#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <vector>

using namespace std;
const int N = 300010;
int fa[N][26];
int depth[N], dist[N];
int tmp[N], pre[N];
int cnt,flag;
//tmp存节点出现的次数,即节点与节点父亲的边在所有路径中出现的次数
//pre存节点与节点父亲之间的边,本题中存的是边权,别的题中也有可能存编号
int n, m;
struct node
{
    int s, t;
    int lca;
} path[N];
int head[N], nex[2 * N], val[2 * N], to[2 * N], idx;
int u, v, w;
void add(int u, int v, int w)
{
    to[idx] = v;
    val[idx] = w;
    nex[idx] = head[u];
    head[u] = idx++;
}
int que[N];

void bfs(int root)
{
    memset(depth, 0x3f, sizeof(depth));
    depth[0]=0;
    depth[root] = 1;
    int h = 0, t = 0;
    que[t++] = root;
    while (h != t)
    {
        int top = que[h++];
        for (int i = head[top]; ~i; i = nex[i])
        {
            int j = to[i];
            int w = val[i];
            if (depth[j] > depth[top] + 1)
            {
                depth[j] = depth[top] + 1;
                dist[j] = dist[top] + val[i];
                que[t++] = j;
                pre[j] = val[i];
                fa[j][0] = top;
                for (int k = 1; k <=21; k ++)
                {
                    fa[j][k] = fa[fa[j][k - 1]][k - 1];
                }
            }
        }
    }
}

int get_lca(int u, int v)
{
    if (depth[u] < depth[v])
    {
        swap(u, v);
    }
    //第一步:先让u跳到与v同一高度
    for (int i = 21; i >= 0; i--)
    {
        if (depth[fa[u][i]] >= depth[v])
        {
            u = fa[u][i];
        }
    }

    if (u == v) //特殊情况,u和v调到同一高度之后重合,只需返回u或v即可
    {
        return u;
    }
    //第二步,让u和v跳到最近公共祖先的下一层
    for (int i = 21; i >= 0; i--)
    {
        if (fa[u][i] != fa[v][i])
        {
            u = fa[u][i];
            v = fa[v][i];
        }
    }
    return fa[u][0]; //再向上跳一步就是root
}

int judge(int a,int f,int cnt,int maxn)
{
    for(int i=head[a];~i;i=nex[i])
    {
        int j=to[i];
        if(j==f)
        {
            continue;
        }
        tmp[a]+=judge(j,a,cnt,maxn);
    }
    if(tmp[a]==cnt&&pre[a]>=maxn)
    {
        flag=1;
    }
    return tmp[a];
}

bool check(int x)
{
    memset(tmp, 0, sizeof(tmp));
    cnt = 0,flag=0;
    int maxn = 0;
    for (int i = 1; i <= m; i++)
    {
        int dis = dist[path[i].s] + dist[path[i].t] - 2 * dist[path[i].lca];
        if (dis > x)
        {
            cnt++;
            maxn = max(maxn, dis - x);
            //s,t加一,最近公共祖先-2
            tmp[path[i].s]++;
            tmp[path[i].t]++;
            tmp[path[i].lca] -= 2;
        }
    }
    if (!cnt)
    {
        return true;
    }
    judge(1,0,cnt,maxn);
    if(flag)
    {
        return true;
    }
    return false;
}

int main()
{
    scanf("%d %d", &n, &m);
    memset(head, -1, sizeof(head));
    for (int i = 0; i < n - 1; i++)
    {
        scanf("%d %d %d", &u, &v, &w);
        add(u, v, w);
        add(v, u, w);
    }
    bfs(1);

    for (int i = 1; i <= m; i++)
    {
        scanf("%d %d", &path[i].s, &path[i].t);
        path[i].lca = get_lca(path[i].s, path[i].t);
    }
    //二分找正确答案
    long long int l = 0, r = 3000000000;
    while (l < r)
    {
        int mid = (l + r) / 2;
        if (check(mid))
        {
            r = mid;
        }
        else
        {
            l = mid + 1;
        }
    }
    cout << l << endl;

    return 0;
}

2.将路径上的所有点权值加一,求最后点的权值。
第二种基本思想见另一篇文章:
https://segmentfault.com/a/11...

03-05 15:22