首页 > 其他分享 >P3384 【模板】重链剖分/树链剖分

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

时间:2024-04-03 12:33:19浏览次数:21  
标签:node 剖分 int top 树链 depth P3384 now id

原题链接

题解

dalao‘s blog

我自己的认识请看代码区

code

#include<bits/stdc++.h>
using namespace std;
int n,Q,root,mod;

int bigson[100005];//和自己在同一条链上的儿子节点
vector<int> G[100005];
int sizes[100005];//主要是为了求子树大小,经过验证选择哪一个儿子节点作为和自己同一条链的要求是可以变的
int fa[100005];//只会在链的开端使用,从一个链跳到另一条链
int depth[100005]={0};//深度是为了在求lca
int id[100005]={0};//id是为了线段树操作,太神了,一个节点的子树内的节点一定是连续的,这样就能直接线段树操作了
int cnt=0;//标记新的id
int a[100005];
int top[100005];//一条链的开端,保证一条链上的新id是连续的,这样就又可以线段树操作了,太神了
int tree[400005];
int lazytag[400005];

void pushdown(int node,int l,int r)
{
    tree[node]+=lazytag[node]*(r-l+1);
    tree[node]%=mod;
    if(l!=r)
    {
        lazytag[node*2]+=lazytag[node];
        lazytag[node*2]%=mod;
        lazytag[node*2+1]+=lazytag[node];
        lazytag[node*2+1]%=mod;
    }
    lazytag[node]=0;
}

void update(int node,int l,int r,int x,int y,int val)
{
    if(lazytag[node]) pushdown(node,l,r);
    if(l>y||r<x) return;
    if(l>=x&&r<=y)
    {
        lazytag[node]+=val;
        lazytag[node]%=mod;
        pushdown(node,l,r);//为什么这里要pushdown,因为要求遍历到的区间都要有真值
        return;
    }

    int mid=(l+r)/2;
    update(node*2,l,mid,x,y,val);
    update(node*2+1,mid+1,r,x,y,val);
    tree[node]=(tree[node*2]+tree[node*2+1])%mod;
}


int query(int node,int l,int r,int x,int y)
{
    if(lazytag[node]) pushdown(node,l,r);
    if(l>y||r<x) return 0;
    if(l>=x&&r<=y)
    {
        return tree[node];
    }

    int mid=(l+r)/2;
    return (query(node*2,l,mid,x,y)+query(node*2+1,mid+1,r,x,y))%mod;
}
void dfs1(int now,int f)
{
    depth[now]=0;
    int maxson=0;
    sizes[now]=1;
    fa[now]=f;
    bigson[now]=0;
    for(auto next:G[now])
    {
        if(next==f) continue;
        dfs1(next,now);
        if(depth[next]>maxson)
        {
            maxson=depth[next];
            bigson[now]=next;
        }
        sizes[now]+=sizes[next];
    }
    depth[now]=maxson+1;
}

void dfs2(int now,int topf)
{
    id[now]=++cnt;
    top[now]=topf;
    if(!bigson[now]) return;
    dfs2(bigson[now],topf);
    for(auto next:G[now])
    {
        if(next==fa[now]||next==bigson[now]) continue;
        dfs2(next,next);
    }
}

void addPath(int x,int y,int v)
{
    while(top[x]!=top[y])
    {
        if(depth[top[x]]>depth[top[y]]) swap(x,y);
        update(1,1,n,id[top[x]],id[x],v);
        x=fa[top[x]];
    }
    if(depth[x]>depth[y]) swap(x,y);
    update(1,1,n,id[y],id[x],v);
}

int sumPath(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(depth[top[x]]>depth[top[y]]) swap(x,y);
        ans+=query(1,1,n,id[top[x]],id[x]);
        ans%=mod;
        x=fa[top[x]];
    }
    if(depth[x]>depth[y])  swap(x,y);
    ans+=query(1,1,n,id[y],id[x]);
    ans%=mod;
    return ans;
}




int main()
{
    cin>>n>>Q>>root>>mod;

    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }

    for(int i=1;i<n;i++)
    {
        int x,y;
        cin>>x>>y;
        G[x].push_back(y);
        G[y].push_back(x);
    }

    dfs1(root,0);
    dfs2(root,root);

    for(int i=1;i<=n;i++) update(1,1,n,id[i],id[i],a[i]);


    while(Q--)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int x,y,v;
            cin>>x>>y>>v;
            v%=mod;
            addPath(x,y,v);
        }
        else if(op==2)
        {
            int x,y;
            cin>>x>>y;
            cout<<sumPath(x,y)<<endl;
        }
        else if(op==3)
        {
            int x,v;
            cin>>x>>v;
            v%=mod;
            update(1,1,n,id[x],id[x]+sizes[x]-1,v);
        }
        else
        {
            int x;
            cin>>x;
            cout<<query(1,1,n,id[x],id[x]+sizes[x]-1)<<endl;
        }
    }
    return 0;
}

标签:node,剖分,int,top,树链,depth,P3384,now,id
From: https://www.cnblogs.com/pure4knowledge/p/18112431

相关文章

  • 树链剖分
    重链剖分【模板】重链剖分/树链剖分upd:每条重链必定由轻儿子开始,不同重链间必定由轻边连接,重链之内必定是重边。如果只有一次询问的话显然可以树上差分来解决,现在考虑多组询问。处理fa[i]dep[i]便于询问。处理size[i]son[i]top[i]idx[i]f[i]便于树剖。明确一下树剖......
  • 【算法与数据结构】二叉树链式结构的实现【前中后序】
    文章目录......
  • 树链剖分【loj模板】(〃>目<)
    小声吐槽:如果不是拍了200000组没问题后瞪眼瞪出来了,我才不写呢Decribe:给定一棵\(n\)个节点的树,初始时该树的根为\(1\)号节点,每个节点有一个给定的权值。下面依次进行\(m\)个操作,操作分为如下五种类型:换根:将一个指定的节点设置为树的新根。修改路径权值:给定两个节点......
  • 树链剖分笔记
    树链剖分+线段树代码量通常在3K左右,出错的地方非常多,为了好好练手,特建立该题单,建议不要进行复制每一题都老老实实重打题单博客题单Orz1.区间加,区间修改,区间最大值#include<bits/stdc++.h>usingnamespacestd;constintMX=1e5+10;intn,m,r,p;intinput[MX]={0};intsiz[......
  • 树链剖分
    树链剖分1基础理论1.1基础概念在树链剖分中,我们将会遇到如下的名词,在此先做以解释:重儿子:对于一个子节点$u$如果$v$是其儿子,且$v$的子树大小是节点$u$的儿子中最大的,则称$v$是$u$的重儿子。轻儿子:除了重儿子以外,就是轻儿子。重链:除顶部以外,其余节点都为重儿子......
  • 树链剖分学习笔记
    树链剖分学习笔记树链剖分的思想及能解决的问题树链剖分用于将树分割成若干条链的形式,以维护树上路径的信息。具体来说,将整棵树剖分为若干条链,使它组合成线性结构,然后用其他的数据结构维护信息。树链剖分(树剖/链剖)有多种形式,如重链剖分,长链剖分和用于Link/cutTree的剖分(......
  • 长链剖分&DP
    长链剖分优化DP长链剖分有一些美妙的性质一个点跳到根最多经过\(\sqrtn\)条链向上跳链,链长一定会增加,最坏是\(1,2,3,...,\sqrtn\)所有长链的总链长相加为n(如说)优化DP如果dp中有一维和深度有关,就考虑优化,考虑用长儿子\(O(1)\)转移(一般是平移,考虑用指针),其他暴力转......
  • 长链剖分
    谁家刚学会一个知识点就放黑来做啊。长链剖分建议在会重链剖分的基础上再来学长链剖分。长链剖分与重链剖分不同的地方在于:重剖以子树大小来选重儿子,长剖以子树深度来选重儿子。长剖的优势有:可以\(\mathcal{O}(1)\)求\(k\)级祖先,可以在与深度有关的树形dp中优化掉一......
  • P5478 [BJOI2015] 骑士的旅行 - 重链剖分
    首先分析一下题目,对于这棵树,操作如下:查询从X到Y的路径上的前k大的值。把$P_i$上的武力值减去一个$F_i$并在Y上的武力值加上一个$F_i$,再把$P_i$改成Y。将$P_i$上的武力值减去一个$F_i$再加上一个Y,并把$F_i$改成Y。由第一个我们可以想到,先用树剖,再用......
  • P3384 【模板】重链剖分/树链剖分 - SGT && 重链剖分
    本题是非常非常非常纯粹的树剖,利用了重链剖分后下标的性质不多说上代码就好了#include<cstdio>#include<vector>#definelllonglongusingnamespacestd;constintN=1e5+5;intn,m,r;llp,a[N],b[N];vector<int>V[N];intfa[N],dep[N],siz[N],hson[N]......