P3384 【模板】轻重链剖分

题目链接

直接贴模板 带注释

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5+7;
const ll inf = 34359738370;
int n,m,r,p;
struct 
{
    int to,next;
}e[maxn<<1];
int head[maxn],num=1;
void add(int v,int u)
{
    e[num].to=u;
    e[num].next=head[v];
    head[v]=num++;
}
int son[maxn],size[maxn],fa[maxn],dep[maxn];
//  重孩子    树的节点个数  父节点   节点深度   
void dfs1(int rt,int par)
{
    size[rt]=1;
    for(int i=head[rt];i;i=e[i].next)
    {
        int to=e[i].to;
        if(to == par) continue;
        fa[to]=rt;
        dep[to]=dep[rt]+1;
        dfs1(to,rt);
        size[rt]+=size[to];
        if(size[to]>size[son[rt]]) son[rt]=to;
    }
}
int top[maxn],pos[maxn],w[maxn],in[maxn],cnt=0;
//  所在链的顶端 dfs序数组 原本的树节点的点权 dfs序的时间戳 时间戳标记
void dfs2(int rt,int t)//t是rt所在链的链顶
{
    pos[++cnt]=rt;
    in[rt]=cnt;
    top[rt]=t;
    if(son[rt]) dfs2(son[rt],t);//优先访问重链 保证重链节点都在dfs序的一段连续区间内
    for(int i=head[rt];i;i=e[i].next)
    {
        int to=e[i].to;
        if(to == fa[rt] || to==son[rt]) continue;
        dfs2(to,to);//访问轻链的时候自己(非重孩子)就是轻链的顶端  重孩子按定义不可以是链顶
    }
}
//树链刨分
int tree[maxn<<2],tag[maxn<<2];//对dfs序建线段树  维护dfs序数组的区间和
inline int lc(int &rt)
{
    return rt<<1;
}
inline int rc(int &rt)
{
    return rt<<1|1;
}
inline void pushup(int rt)
{
    tree[rt]=(tree[lc(rt)]+tree[rc(rt)])%p;
}
inline void change(int rt,int l,int r,int v)//区间[l,r] +v
{
    tree[rt]+=(r-l+1)*v;
    tag[rt]+=v;
}
inline void build(int rt,int l,int r)
{
    if(l == r)
    {
        tree[rt]=w[pos[l]];
        return ;
    }
    int mid=(l+r)>>1;
    build(lc(rt),l,mid);
    build(rc(rt),mid+1,r);
    pushup(rt);
}
inline void pushdown(int rt,int l,int r)
{
    if(tag[rt])
    {
        int mid=(l+r)>>1;
        change(lc(rt),l,mid,tag[rt]);
        change(rc(rt),mid+1,r,tag[rt]);
        tag[rt]=0;
    }
    return ;
}
inline void updata(int rt,int l,int r,int vl,int vr,int v)//区间加v
{
    if(l>vr || r<vl) return ;
    if(vl<=l && r<=vr) 
    {
        change(rt,l,r,v);
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(rt,l,r);
    updata(lc(rt),l,mid,vl,vr,v);
    updata(rc(rt),mid+1,r,vl,vr,v);
    pushup(rt);
}
inline int query(int rt,int l,int r,int vl,int vr)
{
    if(l>vr || r<vl) return 0;
    if(vl<=l && r<=vr) 
    {
        return tree[rt];
    }
    int mid=(l+r)>>1;
    pushdown(rt,l,r);
    return (query(lc(rt),l,mid,vl,vr)+query(rc(rt),mid+1,r,vl,vr))%p;
}
//任意两点之间路上 只能有log条轻链+log条重链  
//求LCA过程就是 把链顶深度大的这个点往上跳到链顶 如果还不在同一链就继续循环该过程 直到2个点在同一个链中,深度小的就是LCA
inline void updata_chain(int x,int y,int z)//x y最短路径上的点 点权+z
{
    int fx=top[x],fy=top[y];
    while(fx != fy) 
    {
        if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy);//保证fx是最深点的链顶
        updata(1,1,n,in[fx],in[x],z);
        x=fa[fx];//fx->x这条链改完了 x变成链顶的父节点 继续修改
        fx=top[x];
    }
    //此时x y在同一条链当中 (深度小的那个点就是原先x、y的LCA 且x,y之间的最短距离就是d[x]+d[y]-2*d[LCA] (d[i]是i到根节点的距离))
    if(in[x]>in[y]) swap(x,y);
    updata(1,1,n,in[x],in[y],z);
    return ;
}
inline int query_chain(int x,int y)
{
    int fx=top[x],fy=top[y],ans=0;
    while(fx != fy)
    {
        if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy);//保证fx是最深点的链顶
        ans=(ans+query(1,1,n,in[fx],in[x]))%p;
        x=fa[fx];//fx->x这条链查询 x变成链顶的父节点 继续查询
        fx=top[x];
    }
    if(in[x] > in[y]) swap(x,y);
    ans=(ans+query(1,1,n,in[x],in[y]))%p;
    return ans;
}
int main()
{
    scanf("%d %d %d %d",&n,&m,&r,&p);
    for(int i=1;i<=n;i++) scanf("%d",w+i);
    for(int i=1;i<n;i++)
    {
        int v,u;
        scanf("%d %d",&v,&u);
        add(v,u),add(u,v);
    }
    dfs1(r,r);//求出重孩子
    dfs2(r,r);//重链划分+求dfs序
    build(1,1,n);
    while(m--)
    {
        int f,x,y,z;
        scanf("%d",&f);
        if(f == 1)//x y链上都加z
        {
            scanf("%d %d %d",&x,&y,&z);
            updata_chain(x,y,z);
        }
        else if(f == 2)//查询x y最短路径上的节点点权和
        {
            scanf("%d %d",&x,&y);
            printf("%d\n",query_chain(x,y));
        }
        else if(f == 3)//修改以x为根的整颗子树
        {
            scanf("%d %d",&x,&z);
            updata(1,1,n,in[x],in[x]+size[x]-1,z);
        }
        else //查询以x为根的整颗子树
        {
            scanf("%d",&x);
            printf("%d\n",query(1,1,n,in[x],in[x]+size[x]-1));
        }
    }
    return 0;
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页