ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树链剖分板子

2021-07-19 09:00:25  阅读:186  来源: 互联网

标签:node 剖分 ll 板子 seg maxn 树链 now sum


\(n\) 个点, \(m\) 个操作数, 根结点为 \(R\), 取模数为 \(mod\)。

输入一颗树。

支持的操作:

  1. 把 \(x\) 点的点权增加(或修改)\(y\)。
  2. 将树从 \(x\) 到 \(y\) 结点最短路径上所有节点的值都加上 \(z\)。
  3. 询问某个节点 \(x\) 到 \(y\) 节点的路径中所有点的点权和 (或maxn)。
  4. 把 \(x\) 为根结点的子树中所有点的点权都增加 \(y\)。
  5. 求以 \(x\) 为根节点的子树内所有节点值之和
const ll N = 3e5 + 4;
ll n, m, tot, R, mod;
ll head[N], fa[N], son[N], siz[N], top[N], dep[N], seg[N], rev[N], a[N], lim[N];

struct nodee
{
    ll to, nxt;
} t[2 * N];

void add(ll x, ll y)
{
    t[++tot].to = y;
    t[tot].nxt = head[x];
    head[x] = tot;
}

void dfs1(ll u, ll father)
{
    siz[u] = 1, fa[u] = father, dep[u] = dep[father] + 1;
    for (ll i = head[u]; i; i = t[i].nxt)
    {
        ll y = t[i].to;
        if (siz[y] == 0)
        {
            dfs1(y, u);
            siz[u] += siz[y];
            if (siz[y] > siz[son[u]])
            {
                son[u] = y;
            }
        }
    }
}

void dfs2(ll u)
{
    seg[u] = ++seg[0];
    rev[seg[0]] = u;
    if (son[u])
    {
        top[son[u]] = top[u];
        dfs2(son[u]);
    }
    for (ll i = head[u]; i; i = t[i].nxt)
    {
        ll y = t[i].to;
        if (y == son[u] || fa[u] == y)
        {
            continue;
        }
        dfs2(y);
    }
    lim[u] = seg[0];
}

struct node
{
    ll L, R, sum, tag, maxn;
    node *lc, *rc;
};

void pushup(node *now)
{
    now->sum = (now->lc->sum + now->rc->sum) % mod;
    now->maxn = max(now->lc->maxn, now->rc->maxn);
}

inline void maketag(node *now, ll w)
{
    now->tag = (now->tag + w) % mod;
    now->sum = (now->sum + (now->R - now->L + 1) * w) % mod;
    now->maxn = w;
}

inline void pushdown(node *now)
{
    if (now->tag == 0)
        return;
    maketag(now->lc, now->tag);
    maketag(now->rc, now->tag);
    now->tag = 0;
}

void build(node *now, ll l, ll r)
{
    now->L = l;
    now->R = r;
    now->tag = 0;
    if (l < r)
    {
        ll mid = (l + r) >> 1;
        now->lc = new node;
        now->rc = new node;
        build(now->lc, l, mid);
        build(now->rc, mid + 1, r);
        pushup(now);
    }
    else
    {
        now->maxn = a[rev[l]];
        now->sum = a[rev[l]];
        now->lc = now->rc = NULL;
    }
}

void change(node *now, ll l, ll r, ll w)
{
    if (l <= now->L and now->R <= r)
    {
        maketag(now, w);
    }
    else if (!((now->L > r) || (now->R < l)))
    {
        pushdown(now);
        change(now->lc, l, r, w);
        change(now->rc, l, r, w);
        pushup(now);
    }
}

ll check1(node *now, ll l, ll r)
{
    if (l <= now->L and now->R <= r)
        return now->sum;
    if ((now->L > r) || (now->R < l))
        return 0;
    pushdown(now);
    return (check1(now->lc, l, r) + check1(now->rc, l, r)) % mod;
}

ll check2(node *now, ll l, ll r)
{
    if (l <= now->L and now->R <= r)
        return now->maxn;
    if ((now->L > r) || (now->R < l))
        return -INF;
    pushdown(now);
    return max(check2(now->lc, l, r), check2(now->rc, l, r));
}

int main()
{
    n = read(), m = read();
    R = 1, mod = INF; // R为根结点序号

    for (ll i = 1; i <= n; i++)
        top[i] = i;

    for (ll i = 1; i <= n; i++)
        a[i] = read();

    for (ll i = 1; i <= n - 1; i++)
    {
        ll x = read(), y = read();
        add(x, y);
        add(y, x);
    }

    dfs1(R, 0);
    dfs2(R);

    node *root;
    root = new node;
    build(root, 1, n);

    for (int i = 1; i <= m; ++i)
    {
        ll opt = read(), x = read(), y, z;

        // 把 x 点的点权增加 y
        // 如果想要修改,更改上面的 maketag
        if (opt == 0) 
        {
            y = read(); 
            change(root, seg[x], seg[x], y);
        }

        // 表示将树从 x 到 y 结点最短路径上所有节点的值都加上 z。
        else if (opt == 1)
        {
            y = read(), z = read();
            while (top[x] != top[y])
            {
                if (dep[top[x]] < dep[top[y]])
                    swap(x, y);
                change(root, seg[top[x]], seg[x], z);
                x = fa[top[x]];
            }
            if(seg[x] > seg[y]) swap(x, y);
            change(root, seg[x], seg[y], z);
        }
        
        // 询问某个节点 x 到 y 节点的路径中所有点的点权和 (或maxn)
        else if (opt == 2)
        {
            y = read();
            ll sum = 0;
            // ll maxn = -2147483647;
            while (top[x] != top[y])
            {
                if (dep[top[x]] < dep[top[y]])
                    swap(x, y);
                sum = (sum + check1(root, seg[top[x]], seg[x])) % mod;
                // maxn = max(maxn, check2(root, seg[top[x]], seg[x]));
                x = fa[top[x]];
            }
            if (seg[x] > seg[y])
                swap(x, y);
            sum = (sum + check1(root, seg[x], seg[y])) % mod;
            // maxn = max(maxn, check2(root, seg[x], seg[y]));
            printf("%lld\n", sum);
        }

        // 把 x 为根结点的子树中所有点的点权都增加 y
        else if (opt == 3) 
        {
            y = read();
            change(root, seg[x], lim[x], y);
        }

        // 求以 x 为根节点的子树内所有节点值之和
        else if (opt == 4)
        {
            ll ans = check1(root, seg[x], lim[x]) % mod;
            printf("%lld\n", ans);
        }
    }
    return 0;
}

标签:node,剖分,ll,板子,seg,maxn,树链,now,sum
来源: https://www.cnblogs.com/EdisonBa/p/14982207.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有