【模板】轻重链剖分+线段树

题目链接

在这里插入图片描述
题解:

#include <bits/stdc++.h>

#define ll long long


using namespace std;
int next[500001];
int head[500001];
int to[500001];
int cnt;

int son[500001];//求重儿子
int fa[500001];//父亲结点
int tol[500001];//子节点个数
int dep[500001];//深度

int dfn[500001];//dfs序列
int rnk[500001];//dfs序列所对应的元素
int top[500001];//链的顶端
int treev[4*500001];

int value[500001];
int n,m,r,p;

int lazy[4*500001];
void pushdown(int l,int r,int x);

int query(int l,int r,int lx,int ly,int x);
void add(int u,int v)
{
    next[++cnt]=head[u];
    head[u]=cnt;
    to[cnt]=v;
}
void dfs1(int u,int v)
{
    son[u]=-1;
    tol[u]=1;
    fa[u]=v;
    dep[u]=dep[v]+1;
    for (int i=head[u];i;i=next[i])
    {
        int t=to[i];
        if(t==v) continue ;
        dfs1(t,u);
        tol[u]+=tol[t];
        if(son[u]==-1||(tol[t]>tol[son[u]])) son[u]=t;
    }
}
void dfs2(int u,int t) //t为链的顶端
{
    top[u]=t;
    dfn[u]=++cnt;
    rnk[cnt]=u;
    if(son[u]==-1) return ;
    dfs2(son[u],t);
    for (int i=head[u];i;i=next[i])
    {
        if(to[i]!=fa[u]&&to[i]!=son[u]) //不为父亲和重儿子
         dfs2(to[i],to[i]); //轻链顶端就是本身
    }
}
void build(int l,int r,int x)
{
    if(l==r)
    {
        treev[x]=value[rnk[l]]%p;
        return ;
    }
    int mid=l+r>>1;
    build(l,mid,x<<1);
    build(mid+1,r,x<<1|1);
    treev[x]=(treev[x<<1]+treev[x<<1|1])%p;
}
void update1(int l,int r,int lx,int ly,int x,int z)
{
    if(lx<=l&&r<=ly)
    {
        treev[x]+=z*(r-l+1);
        lazy[x]+=z;
        return ;
    }
    int mid=l+r>>1;
    if(lazy[x]) pushdown(l,r,x);
    if(lx<=mid) update1(l,mid,lx,ly,x<<1,z);
    if(ly>mid) update1(mid+1,r,lx,ly,x<<1|1,z);
      treev[x]=(treev[x<<1]+treev[x<<1|1])%p;

}
void lca(int x,int y,int z)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])

          {

            update1(1,n,dfn[top[y]],dfn[y],1,z);
            y=fa[top[y]];
          }
        else
           {

           update1(1,n,dfn[top[x]],dfn[x],1,z);
            x=fa[top[x]];

           }
    }
    if(dep[x]<dep[y])
         update1(1,n,dfn[x],dfn[y],1,z);
    else
         update1(1,n,dfn[y],dfn[x],1,z);
}
int sum(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
         if(dep[top[x]]<dep[top[y]])
           {

            ans+=query(1,n,dfn[top[y]],dfn[y],1);
             ans=ans%p;
            y=fa[top[y]];
           }
        else

           {

           ans+=query(1,n,dfn[top[x]],dfn[x],1);
            ans=ans%p;
            x=fa[top[x]];
           }
    }
    if(dep[x]<dep[y])
        ans+=query(1,n,dfn[x],dfn[y],1);
    else
        ans+=query(1,n,dfn[y],dfn[x],1);
    return ans%p;;
}
void pushdown(int l,int r,int x)
{
    lazy[x<<1]=(lazy[x<<1]+lazy[x])%p;
    lazy[x<<1|1]=(lazy[x<<1|1]+lazy[x])%p;
    treev[x<<1]=(treev[x<<1]+lazy[x]*((l+r>>1)-l+1))%p;
    treev[x<<1|1]=(treev[x<<1|1]+lazy[x]*(r-(l+r>>1)))%p;
    lazy[x]=0;
}
int query(int l,int r,int lx,int ly,int x)
{
    if(lx<=l&&r<=ly)
    {
      //  cout<<treev[x]<<endl;
        return treev[x];

    }
    int mid=l+r>>1;
    int ans=0;
    if(lazy[x]) pushdown(l,r,x);
    if(lx<=mid) ans=(ans+query(l,mid,lx,ly,x<<1))%p;
    if(ly>mid) ans=(ans+query(mid+1,r,lx,ly,x<<1|1))%p;
    return ans;
}
int main()
{
   cin>>n>>m>>r>>p;
   for (int i=1;i<=n;i++)
   {
       cin>>value[i];
   }
   for (int i=0;i<n-1;i++)
   {
       int a,b;
       cin>>a>>b;
       add(a,b);
       add(b,a);
   }
   dfs1(r,r);
   cnt=0;
   dfs2(r,r);
   build(1,n,1);
   for (int i=0;i<m;i++)
   {
       int op;
       cin>>op;
       if(op==1)
       {
           int x,y,z;
           cin>>x>>y>>z;
           lca(x,y,z);
       }
       else if(op==2)
       {
           int x,y;
           cin>>x>>y;
           cout<<sum(x,y)<<endl;
       }
       else if(op==3)
       {
           int x,z;
           cin>>x>>z;
          // cout<<dfn[x]<<"      "<<tol[x]<<endl;
           int y=dfn[x]+tol[x]-1;
           update1(1,n,dfn[x],y,1,z);
       }
       else{
            int x;
            cin>>x;
            int y=dfn[x]+tol[x]-1;
            cout<<query(1,n,dfn[x],y,1)<<endl;
       }
   }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值