1. 程式人生 > 實用技巧 >P3384 輕重鏈剖分

P3384 輕重鏈剖分

複雜資料結構++

樹鏈剖分,極大地提高了樹上元素的查詢修改的效率。

這裡是輕重鏈剖分,通過將對樹的dfs,在求出每個節點的父親,深度,子樹大小,重兒子編號的同時,把樹分割成了若干條重鏈,每條鏈都有獨一無二的top節點

然後將這些鏈逐一標號對映到序列中,在輕重鏈剖分下每一個節點及其子樹的下表在序列中連續。

因此就可以通過線段樹之類的資料結構在log級別的時間內修改查詢

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
double pi = acos(-1);
const double eps = 1e-12;
const int maxn = 1e5 + 10;
const int inf = 1e9;
int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
    return f * x;
}
ll a[maxn], wt[maxn], mod;
int n, m, S;
int head[maxn], deep[maxn], fa[maxn], siz[maxn], son[maxn], id[maxn], top[maxn], edge_cnt = 0, cnt = 0;

struct edge {
    int to, next;
}e[2 * maxn];

inline void add(int from, int to)
{
    e[++edge_cnt].to = to;
    e[edge_cnt].next = head[from];
    head[from] = edge_cnt;
}

void dfs1(int now, int f, int dee)//先記錄每個點的深度,子樹大小,父節點編號,重兒子編號
{
    deep[now] = dee;
    fa[now] = f;
    siz[now] = 1;
    int maxson = -1;
    for (int i = head[now]; i; i = e[i].next)
    {
        int to = e[i].to;
        if (to == f)continue;
        dfs1(to, now, dee + 1);
        siz[now] += siz[to];
        if (siz[to] > maxson)son[now] = to, maxson = siz[to];
    }
}

void dfs2(int now, int topf)
{
    id[now] = ++cnt;
    wt[cnt] = a[now];//將節點值對映到序列
    top[now] = topf;
    if (!son[now])return;
    dfs2(son[now], topf);//先處理重兒子
    for (int i = head[now]; i; i = e[i].next)
    {
        int to = e[i].to;
        if (to == fa[now] || to == son[now])continue;
        dfs2(to, to);//遍歷輕兒子,然而輕兒子還是會先遍歷重邊,所以每一條重鏈新編號是連續的
    }
    //最終子樹的編號也是連續的,因此可以用線段樹查詢子樹和
}

struct tree {
    int ls;
    int rs;
    long long vue = 0;
    long long lazy = 0;
}t[4 * maxn];
void build(int l, int r, int p)
{
    t[p].ls = l;
    t[p].rs = r;
    if (l == r) { t[p].vue = wt[l] % mod; return; }
    int mid = l + r >> 1;
    build(l, mid, p << 1);
    build(mid + 1, r, (p << 1) + 1);
    t[p].vue = (t[p << 1].vue + t[(p << 1) + 1].vue) % mod;
}
inline void spread(int x)//標記下傳
{
    if (t[x].lazy)//如果該節點標記不為0
    {
        t[x * 2].vue += t[x].lazy * (ll)(t[x * 2].rs - t[x * 2].ls + 1);//更新左子區間和
        t[x * 2 + 1].vue += t[x].lazy * (ll)(t[x * 2 + 1].rs - t[x * 2 + 1].ls + 1);//更新右子區間和
        t[x * 2].lazy += t[x].lazy;
        t[x * 2 + 1].lazy += t[x].lazy;//向子節點傳遞標記
        t[x].lazy = 0;//初始化標記
    }
}
void update_add(int x, int y, ll v, int p)
{
    ll l = t[p].ls;
    ll r = t[p].rs;
    if (l >= x && r <= y)
    {
        t[p].vue += (long long)v * (r - l + 1);
        t[p].lazy += v;
        return;
    }
    spread(p);
    int mid = l + r >> 1;
    if (x <= mid)update_add(x, y, v, p << 1);
    if (y > mid)update_add(x, y, v, (p << 1) + 1);
    t[p].vue = t[p << 1].vue + t[(p << 1) + 1].vue;
}

long long query(int x, int y, int p)
{
    int l = t[p].ls;
    int r = t[p].rs;
    if (l >= x && r <= y)
    {
        return t[p].vue % mod;
    }
    int mid = l + r >> 1;
    long long ans = 0;
    spread(p);
    if (x <= mid)ans = (ans + query(x, y, p << 1)) % mod;
    if (y > mid)ans = (ans + query(x, y, (p << 1) + 1)) % mod;
    return ans;
}

void update_range(int x, int y,ll v)
{
    v %= mod;
    while (top[x] != top[y])
    {
        if (deep[top[y]] > deep[top[x]])swap(x, y);
        update_add(id[top[x]], id[x], v, 1);
        x = fa[top[x]];
    }
    if (deep[x] > deep[y])swap(x, y);
    update_add(id[x], id[y], v, 1);
}

ll query_range(int x,int y)
{
    ll res = 0;
    while (top[x] != top[y])
    {
        if (deep[top[y]] > deep[top[x]])swap(x, y);
        res = (res + query(id[top[x]], id[x], 1)) % mod;
        x = fa[top[x]];
    }
    if (deep[x] > deep[y])swap(x, y);
    res = (res + query(id[x], id[y], 1)) % mod;
    return res;
}

int main()
{
    fastio;
    cin >> n >> m >> S >> mod;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i < n; i++)
    {
        int x, y;
        cin >> x >> y;
        add(x, y);
        add(y, x);
    }
    dfs1(S, 0, 1);
    dfs2(S, S);
    build(1, n, 1);
    ll o, x, y, z;
    while (m--)
    {
        cin >> o;
        if (o == 1)
        {
            cin >> x >> y >> z;
            update_range(x, y, z);
        }
        else if (o == 2)
        {
            cin >> x >> y;
            cout << query_range(x, y) << endl;
        }
        else if (o == 3)
        {   
            cin >> x >> z;
            update_add(id[x], id[x] + siz[x] - 1, z, 1);
        }
        else
        {
            cin >> x;
            cout << query(id[x], id[x] + siz[x] - 1, 1) << endl;
        }
    }
    return 0;

}