P3384 【模板】重链剖分/树链剖分

这篇文章介绍了如何使用C++编程语言,结合深度优先搜索(DFS)和线段树的数据结构,进行区间修改和查询的操作,解决与图论相关的问题,如在给定的树结构中高效更新和查询特定范围内的值。
摘要由CSDN通过智能技术生成

 学习链接:https://zhuanlan.zhihu.com/p/41082337

其实就是运用了两边dfs维护dfs序之类的信息,然后线段树区间修改,区间查询之类的操作 

#include <bits/stdc++.h>
using namespace std;
#define pi acos(-1)
#define xx first
#define yy second
#define endl "\n"
#define lowbit(x) x & (-x)
#define int long long
#define ull unsigned long long
#define pb push_back
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define LF(x) fixed << setprecision(x)
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define Yshanqian ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 1e5 + 10, M = 1010, inf = 0x3f3f3f3f, P = 13331;
const double eps = 1e-8;
int n, m, r, p, cnt, mod;
int a[N];
int son[N], id[N], top[N], siz[N], f[N], rk[N], d[N];
vector<int> g[N];
struct node
{
    int l, r, sum;
    int lazy;
} tr[N << 2];
void dfs1(int u, int fa, int depth)
{
    siz[u] = 1;
    f[u] = fa; // 自己的父亲赋为自己
    d[u] = depth;
    for (auto ed : g[u])
    {
        if (ed == fa)
            continue;
        dfs1(ed, u, depth + 1);
        siz[u] += siz[ed];
        if (siz[ed] > siz[son[u]])
            son[u] = ed;
    }
}
void dfs2(int u, int t)
{
    top[u] = t;
    id[u] = ++cnt; // dfs徐的编号
    rk[cnt] = u;   // dfs序编号对应的原始点编号
    if (!son[u])   // 叶子节点
        return;
    dfs2(son[u], t);
    for (auto ed : g[u])
    {
        if (son[u] != ed && ed != f[u])
            dfs2(ed, ed); // 一个点位于轻链底端,那么它的top必然是它本身
    }
}
void change(int u, int k)
{
    tr[u].sum = ((tr[u].sum + (tr[u].r - tr[u].l + 1) * k % mod) % mod + mod) % mod;
    tr[u].lazy += k;
}
void pushdown(int u)
{
    if (tr[u].lazy)
    {
        change(u << 1, tr[u].lazy);
        change(u << 1 | 1, tr[u].lazy);
        tr[u].lazy = 0;
    }
}
void pushup(int u)
{
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r)
{
    tr[u] = {l, r, 0, 0};
    if (l == r)
    {
        tr[u] = {l, r, a[rk[l]], 0};
        return;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}
void modify(int u, int l, int r, int x)
{
    if (tr[u].l >= l && tr[u].r <= r)
    {
        change(u, x);
        return;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid)
        modify(u << 1, l, r, x);
    if (r > mid)
        modify(u << 1 | 1, l, r, x);
    pushup(u);
}
int query(int u, int l, int r)
{
    if (tr[u].l >= l && tr[u].r <= r)
    {
        return tr[u].sum % mod;
    }
    pushdown(u);
    int ans = 0;
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid)
        ans = (ans + query(u << 1, l, r)) % mod;
    if (r > mid)
        ans = (ans + query(u << 1 | 1, l, r)) % mod;
    return ans;
}
void modifys(int x, int y, int c)
{
    int fx = top[x], fy = top[y];
    while (fx != fy) // 两点不在同一条重链
    {
        if (d[fx] >= d[fy]) // 先看顶点得高度,来判断lca
        {
            modify(1, id[fx], id[x], c);
            x = f[fx], fx = top[x]; // 低点得先跳,
        }
        else
        {
            modify(1, id[fy], id[y], c);
            y = f[fy], fy = top[y]; // 将x设置成原链头的父亲结点,走轻边,继续循环
        }
    }
    // 循环结束,两点位于同一重链上,但两点不一定为同一点,所以我们还要统计这两点之间的贡献
    if (id[x] <= id[y])
        modify(1, id[x], id[y], c);
    else
        modify(1, id[y], id[x], c);
}
int sum(int x, int y)
{
    int ans = 0;
    int fx = top[x], fy = top[y];
    while (fx != fy)
    {
        if (d[fx] >= d[fy])
        {
            ans = (ans + query(1, id[fx], id[x])) % mod;
            x = f[fx], fx = top[x];
        }
        else
        {
            ans = (ans + query(1, id[fy], id[y])) % mod;
            y = f[fy], fy = top[y];
        }
    }

    if (id[x] <= id[y])
        ans = (ans + query(1, id[x], id[y])) % mod;
    else
        ans = (ans + query(1, id[y], id[x])) % mod;
    return ans;
}
void solve()
{
    cin >> n >> m >> r >> mod;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= n - 1; i++)
    {
        int a, b;
        cin >> a >> b;
        g[a].pb(b);
        g[b].pb(a);
    }
    dfs1(r, 0, 1);
    dfs2(r, r);
    build(1, 1, n);
    for (int i = 1; i <= m; i++)
    {
        int op, x, y, z;
        cin >> op;
        if (op == 1)
        {
            cin >> x >> y >> z;
            modifys(x, y, z);
        }
        else if (op == 2)
        {
            cin >> x >> y;
            cout << sum(x, y) << endl;
        }
        else if (op == 3)
        {
            cin >> x >> z;
            int l = id[x];
            int r = l + siz[x] - 1;
            modify(1, l, r, z);
        }
        else
        {
            cin >> x;
            int l = id[x];
            int r = l + siz[x] - 1;
            cout << query(1, l, r) % mod << endl;
        }
    }
}
signed main()
{
    Yshanqian;
    int T;
    T = 1;
    // cin >> T;
    for (int cases = 1; cases <= T; ++cases)
    {
        // cout<<"Case #"<<cases<<": ";
        solve();
    }
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值