树链剖分——其实不难

直接放博客——原理及一些概念

         树链剖分详解(洛谷模板 P3384)

    ————来自 ChinHhh 大佬的博客 ——写得真的很好(我也是看这篇看懂的)

 

代码—— 

  

#include <bits/stdc++.h>
using namespace std;

#define ls p << 1
#define rs p << 1 | 1
const int maxn = 2 * 1e6 + 10;

int read() //普通快读
{
    int f = 1, k = 0;
    char c = getchar();
    while (c != '-' && (c < '0' || c > '9')) {
        c = getchar();
    }
    if (c == '-') {
        f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        k = (k << 3) + (k << 1) + (c ^ 48);
        c = getchar();
    }
    return f * k;
}

struct node {
    int u, v, nxt;
} edge[maxn];
int first[maxn];
int num = 0;
//这里存初始的树,用的邻接表

struct tree {
    int l, r, sum, siz, lazy;
} t[maxn * 2];
//这里存线段树:
//l,r:左右儿子。 sum:区间和。 siz:以当前节点为根的子树大小(包括了自己)。 lazy:懒标记。

int n, m, root, mod, cnt = 0, a[maxn], b[maxn];

void add(int x, int y)
{
    num++;
    edge[num].u = x;
    edge[num].v = y;
    edge[num].nxt = first[x];
    first[x] = num;
}

int deep[maxn], fa[maxn], son[maxn], tot[maxn], top[maxn], id[maxn];

int dfs1(int now, int f, int dep)
{
    deep[now] = dep;
    fa[now] = f;
    tot[now] = 1;
    int maxson = -1;
    for (int i = first[now]; i != -1; i = edge[i].nxt) {
        if (edge[i].v == f)
            continue;
        tot[now] += dfs1(edge[i].v, now, dep + 1);
        if (tot[edge[i].v] > maxson)
            maxson = tot[edge[i].v], son[now] = edge[i].v;
    }
    return tot[now];
}
//找重儿子

void dfs2(int now, int topf)
{
    id[now] = ++cnt;
    a[cnt] = b[now];
    top[now] = topf;
    if (!son[now])
        return;
    dfs2(son[now], topf);
    for (int i = first[now]; i != -1; i = edge[i].nxt)
        if (!id[edge[i].v])
            dfs2(edge[i].v, edge[i].v);
}
//处理重链

//-------------------以下为线段树
void pushup(int p)
{
    t[p].sum = (t[ls].sum + t[rs].sum + mod) % mod;
}

void build(int p, int ll, int rr)
{
    t[p].l = ll;
    t[p].r = rr;
    t[p].siz = rr - ll + 1;
    if (ll == rr) {
        t[p].sum = a[ll];
        return;
    }
    int mid = (ll + rr) >> 1;
    build(ls, ll, mid);
    build(rs, mid + 1, rr);
    pushup(p);
}

void pushdown(int p)
{
    if (!t[p].lazy)
        return;
    t[ls].sum = (t[ls].sum + t[ls].siz * t[p].lazy) % mod;
    t[rs].sum = (t[rs].sum + t[rs].siz * t[p].lazy) % mod;
    t[ls].lazy = (t[ls].lazy + t[p].lazy) % mod;
    t[rs].lazy = (t[rs].lazy + t[p].lazy) % mod;
    t[p].lazy = 0;
}

void intervaladd(int p, int ll, int rr, int val) //修改子树
{
    if (ll <= t[p].l && t[p].r <= rr) {
        t[p].sum += t[p].siz * val;
        t[p].lazy += val;
        return;
    }
    pushdown(p);
    int mid = (t[p].l + t[p].r) >> 1;
    if (ll <= mid)
        intervaladd(ls, ll, rr, val);
    if (rr > mid)
        intervaladd(rs, ll, rr, val);
    pushup(p);
} //由于我们处理过了,所以这里的以p为根的子树的编号是连续的,故可直接用线段树的操作进行修改(相当于一个小线段树)

void treeadd(int x, int y, int val)
{
    while (top[x] != top[y]) {
        if (deep[top[x]] < deep[top[y]])
            swap(x, y);
        intervaladd(1, id[top[x]], id[x], val);
        x = fa[top[x]];
    }
    if (deep[x] > deep[y])
        swap(x, y);
    intervaladd(1, id[x], id[y], val);
} //想着LCA就懂了,只不过这里用的是重链

int intervalquery(int p, int ll, int rr) //求子树的和
{
    int ans = 0;
    if (ll <= t[p].l && t[p].r <= rr)
        return t[p].sum;
    pushdown(p);
    int mid = (t[p].l + t[p].r) >> 1;
    if (ll <= mid)
        ans = (ans + intervalquery(ls, ll, rr)) % mod;
    if (rr > mid)
        ans = (ans + intervalquery(rs, ll, rr)) % mod;
    return ans;
} //由于我们处理过了,所以这里的以p为根的子树的编号是连续的,故可直接用线段树的操作进行修改(相当于一个小线段树)

int treequery(int x, int y) //求最短路径的和,依旧和LCA差不多
{
    int ans = 0;
    while (top[x] != top[y]) {
        if (deep[top[x]] < deep[top[y]])
            swap(x, y);
        ans = (ans + intervalquery(1, id[top[x]], id[x])) % mod;
        x = fa[top[x]];
    }
    if (deep[x] > deep[y])
        swap(x, y);
    ans = (ans + intervalquery(1, id[x], id[y])) % mod;
    return ans;
}

int main()
{
    memset(first, -1, sizeof(first));

    n = read();
    m = read();
    root = read();
    mod = read();
    for (int i = 1; i <= n; i++) {
        b[i] = read();
    }
    for (int i = 1; i <= n - 1; i++) {
        int x = read();
        int y = read();
        add(x, y);
        add(y, x);
    }

    dfs1(root, 0, 1);
    dfs2(root, root);
    build(1, 1, n);

    while (m--) {
        int op = read();
        int x, y, z;
        if (op == 1) {
            x = read();
            y = read();
            z = read();
            z = z % mod;
            treeadd(x, y, z);
        } else if (op == 2) {
            x = read();
            y = read();
            printf("%d\n", treequery(x, y));
        } else if (op == 3) {
            x = read();
            z = read();
            intervaladd(1, id[x], id[x] + tot[x] - 1, z % mod);
        } else if (op == 4) {
            x = read();
            printf("%d\n", intervalquery(1, id[x], id[x] + tot[x] - 1));
        }
    }

    return 0;
}

 

  

 

posted @ 2020-10-22 13:38  everlasting_k  阅读(141)  评论(0)    收藏  举报
Live2D