P3384 轻重链剖分(树剖模板)

题目描述


如题,已知一棵包含 NN 个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:

操作 1: 格式: 1 x y z 表示将树从 x 到 y 结点最短路径上所有节点的值都加上 z。

操作 2: 格式:2 x y 表示求树从 x 到 y 结点最短路径上所有节点的值之和。

操作 3: 格式: 3 x z 表示将以 x 为根节点的子树内所有节点值都加上 z。

操作 4: 格式: 4 x 表示求以 x 为根节点的子树内所有节点值之和

输入格式)

第一行包含 4 个正整数 N,M,R,P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。

接下来一行包含 N 个非负整数,分别依次表示各个节点上初始的数值。

接下来 N−1 行每行包含两个整数 x,y,表示点 x 和点 y 之间连有一条边(保证无环且连通)。

接下来 MM 行每行包含若干个正整数,每行表示一个操作,格式如下:

操作 1:1 x y z;

操作 2: 2 x y;

操作 3:3 x z;

操作 4: 4 x。

输出格式
输出包含若干行,分别依次表示每个操作2 或操作 4 所得的结果(对 P 取模)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx=1e5+10;
const int MAXN=2*1e6+10;
const double PI=cos(-1.0);
int dep[mx],top[mx],w[mx],son[mx],id[mx],siz[mx],fa[mx],res=0,cnt=0,a[mx];
int qr[mx<<2],laz[mx<<2];
int n,m,r,p;
vector<int>G[mx];
#define Temp template<typename T>
inline char nc()
{
    static char buf[MAXN],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXN,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    char c=nc();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=nc();}
    while(c>='0'&&c<='9'){x=x*10+c-'0',c=nc();}
    return x*f;
}
#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define len (r-l+1)
inline void pushdown(int rt,int lenn)
{
    laz[rt<<1]+=laz[rt];
    laz[rt<<1|1]+=laz[rt];
    qr[rt<<1]+=laz[rt]*(lenn-(lenn>>1));
    qr[rt<<1|1]+=laz[rt]*(lenn>>1);
    qr[rt<<1]%=p;
    qr[rt<<1|1]%=p;
    laz[rt]=0;
}
inline void build(int rt,int l,int r)
{
    if(l==r)
    {
        qr[rt]=w[l];
        if(qr[rt]>p) qr[rt]%=p;
        return ;
    }
    build(lson);
    build(rson);
    qr[rt]=(qr[rt<<1]+qr[rt<<1|1])%p;
}
inline void query(int rt,int l,int r,int L,int R){
    if(L<=l&&r<=R){res+=qr[rt];res%=p;return;}
    else{
        if(laz[rt])pushdown(rt,len);
        if(L<=mid)query(lson,L,R);
        if(R>mid)query(rson,L,R);
    }
}
inline void updata(int rt,int l,int r,int L,int R,int k)
{
    if(L<=l&&r<=R)
    {
        laz[rt]+=k;
        qr[rt]+=k*len;
    }
    else{
        if(laz[rt]) pushdown(rt,len);
        if(L<=mid) updata(lson,L,R,k);
        if(R>mid) updata(rson,L,R,k);
        qr[rt]=(qr[rt<<1]+qr[rt<<1|1])%p;
    }
}
inline int qrange(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        res=0;
        query(1,1,n,id[top[x]],id[x]);
        ans=(ans+res)%p;
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);res=0;
    query(1,1,n,id[x],id[y]);
    ans+=res;
    return ans%p;
}
inline int upqrange(int x,int y,int k)
{
    k%=p;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        updata(1,1,n,id[top[x]],id[x],k);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    updata(1,1,n,id[x],id[y],k);

}
inline void dfs(int u,int fath,int deep)
{
     fa[u]=fath;
     dep[u]=deep;
     siz[u]=1;
     int maxone=-1;
     for(int i=0;i<G[u].size();i++)
     {
         int to=G[u][i];
         if(to==fath) continue;
         dfs(to,u,deep+1);
         siz[u]+=siz[to];
         if(siz[to]>maxone)
         {
             maxone=siz[to];
             son[u]=to;
         }
     }
}
inline void dfs2(int x,int fst)
{
    top[x]=fst;
    id[x]=++cnt;
    w[cnt]=a[x];
    if(!son[x]) return;
    dfs2(son[x],fst);
    for(register int i=0;i<G[x].size();i++)
    {
        int v=G[x][i];
        if(v==fa[x]||v==son[x]) continue;
        dfs2(v,v);
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&r,&p);
//    n=read(),m=read(),r=read(),p=read();
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);

    for(int i=1,x,y;i<n;i++)
    {
        scanf("%d%d",&x,&y);
//        x=read();y=read();
        G[x].push_back(y);
        G[y].push_back(x);
    }
    dfs(r,0,1);
    dfs2(r,r);
    build(1,1,n);
    for(int i=1,x,y,z,q;i<=m;i++)
    {
        scanf("%d",&q);
        if(q==1)
        {
            scanf("%d%d%d",&x,&y,&z);
//            x=read();y=read();z=read();
            upqrange(x,y,z);
        }
        else if(q==2)
        {
           scanf("%d%d",&x,&y);
            printf("%d\n",qrange(x,y));
        }
        else if(q==3)
        {
            scanf("%d%d",&x,&z);
           // x=read();z=read();
            updata(1,1,n,id[x],id[x]+siz[x]-1,z);
        }
        else
        {
            scanf("%d",&x);
            res=0;query(1,1,n,id[x],id[x]+siz[x]-1);
            printf("%d\n",res);
        }
    }
   return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值