救命,调了一下午28pts

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

niweizheni @ 2024-07-10 17:25:13

求大佬看看错在哪里了,调了一下午 28pts #2 #3 #11 AC 其余全wa

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+15;
int n,m,r,p;

int h[N*2],to[N*2],ne[2*N],idx=1;
int a[N*2],aid[N*2];//初始值 
int sum[N*4],lazy[N*4],ll[N*4],rr[N*4];//线段树数组(和,懒标记) 
int son[N*2],id[N*2],fa[N*2],cnt=0,dep[N*2],ssize[N*2],top[N*2]; 
//son表示他的重儿子,id表示他的编号,fa表示他的父亲节点,cnt是编号记录器,dep是他的深度,ssize是他儿子加上他自己的大小,top是他重链的顶端
int ans=0,res=0;//答案 

void add(int u,int v)//链式前向星 
{
    to[idx]=v;
    ne[idx]=h[u];
    h[u]=idx++;
}

//向上累加
void push_up(int x)
{
    sum[x]=(sum[x*2]%p+sum[x*2+1]%p)%p;
}

//懒标记下放
void push_down(int x)
{
    if(lazy[x])
    {
        lazy[x*2]=lazy[x];
        lazy[x*2+1]=lazy[x];
        sum[x*2]+=lazy[x]*(rr[x*2]-ll[x*2]+1);
        sum[x*2]%=p;
        sum[x*2+1]+=lazy[x]*(rr[x*2+1]-ll[x*2+1]+1);
        sum[x*2+1]%=p;
        lazy[x]=0;
    }
}

//建立线段树
void build(int x,int l,int r)
{
    lazy[x]=0;
    ll[x]=l;
    rr[x]=r;
    if(l==r)
    {
        //cout<<aid[l]<<endl;
        sum[x]=aid[l];//如果是叶子节点就赋值回溯 
        sum[x]%=p;
        return;
    }
    int mid=(l+r)>>1;
    build(x*2,l,mid);//找左儿子 
    build(x*2+1,mid+1,r);//找右儿子 
    push_up(x);
}

//区间修改 
void update(int x,int l,int r,int k)
{
    //cout<<x<<endl;
    //cout<<l<<" "<<ll[x]<<" "<<rr[x]<<" "<<r<<" "<<x<<endl;
    if(l<=ll[x]&&rr[x]<=r)
    {
        lazy[x]+=k;
        sum[x]+=k*(rr[x]-ll[x]+1);
        sum[x]%=p;
        return;
    }
    push_down(x);
    int mid=(ll[x]+rr[x])/2;
    if(l<=mid)
    {
        update(x*2,l,r,k);
    }
    if(r>mid)
    {
        update(x*2+1,l,r,k);
    }
    push_up(x);
}

//区间查询
void query(int x,int l,int r)
{   
    //cout<<x<<endl;
    if(l<=ll[x]&&rr[x]<=r)
    {
        //cout<<sum[x]<<endl;
        res+=sum[x];
        res%=p;
        return;
    }
    push_down(x);
    int mid=(ll[x]+rr[x])>>1;
    if(l<=mid)
    {
        query(x*2,l,r);
    }
    if(r>mid)
    {
        query(x*2+1,l,r);
    }
}

//------------------------------上为线段树,下为重链剖分----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

//第一次深搜,预处理出son,dep,fa,ssize
void dfs(int u,int f,int deep)
{
    //cout<<u<<" ";
    dep[u]=deep;//预处理dep 
    fa[u]=f;//预处理fa
    ssize[u]=1;//把自己的子树加上自己 
    int maxson=-1;
    for(int i=h[u];i!=-1;i=ne[i])//用链式前向星循环 
    {
        int v=to[i];
        if(v==f)//不能往回走,避免卡住 
        {
            continue;
        }
        dfs(v,u,deep+1);//深搜   
        ssize[u]+=ssize[v];//加上自己的儿子的子树大小(预处理ssize) 
        if(ssize[v]>maxson)
        {
            son[u]=v;//预处理son 
            maxson=ssize[v];
        }
    }

}

//第二次深搜,预处理id,aid,top
void dp_dfs(int u,int topf)//topf是u所在树链开始地点(顶点)
{
    //cout<<u<<" ";
    id[u]=++cnt;//记录新点编号
    aid[cnt]=a[u];//记录新点点权 
    top[u]=topf;//记录所在树链起点
    if(!son[u])
    {
        return;//没有儿子就返回 
    }
    dp_dfs(son[u],topf);//继续找重儿子 
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int v=to[i];
        if(v==fa[u]||v==son[u])//判断轻儿子 
        {
            continue;
        }
        dp_dfs(v,v);//每个轻儿子都有以自己为顶点的子树 
    }
}

void updrange(int x,int y,int z)//区间修改(做和) 
{
    z%=p;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
        {
            swap(x,y);
        }
        update(1,id[top[x]],id[x],z);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])
    {
        swap(x,y);
    }
    update(1,id[x],id[y],z);
}

//
int qrange(int x,int y)
{
    ans=0;
    while(top[x]!=top[y])
    {
        res=0;
        if(dep[top[x]]<dep[top[y]])
        {
            swap(x,y);
        }
        query(1,id[top[x]],id[x]);
        ans+=res;
        ans%=p;
        x=fa[top[x]];
    }
    res=0;
    if(dep[x]>dep[y])
    {
        swap(x,y);
    }
    query(1,id[x],id[y]);
    ans+=res;
    ans%=p;
    return ans;
}

//更改子树值 
void upson(int x,int k)
{
    update(1,id[x],id[x]+ssize[x]-1,k);
}

//子树和相加 
int qson(int x)
{
    res=0;
    query(1,id[x],id[x]+ssize[x]-1);
    return res;
}

int main()
{
    memset(h,-1,sizeof(h));
    scanf("%d%d%d%d",&n,&m,&r,&p);
    for(int i=1;i<=n;i++)//输入每个点的点权
    {
        cin>>a[i];

    }
    for(int i=1;i<n;i++)//建树 
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);//使用链式前向星 
        add(y,x);
    }
    dfs(r,0,1);//执行第一次深搜,预处理出son,dep,fa,ssize
    //
    dp_dfs(r,r);//执行第二次深搜,预处理出id,top,aid
    //cout<<1;
    build(1,1,n);//建立线段树 
    for(int i=1;i<=m;i++)
    {
        int q;
        scanf("%d",&q);
        if(q==1)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            updrange(x,y,z);
        }
        else if(q==2)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d\n",qrange(x,y));
        }
        else if(q==3)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            upson(x,y);
        }
        else if(q==4)
        {
            int x;
            scanf("%d",&x);
            printf("%d\n",qson(x));
        }
    }
    return 0;
}

by Little_Cancel_Sunny @ 2024-07-10 17:30:32

注意此处lazy数组应该是加上父亲节点的lazy,而不是等于

lazy[x*2]+=lazy[x];
lazy[x*2+1]+=lazy[x];

by niweizheni @ 2024-07-10 17:31:24

谢谢DaLao


by niweizheni @ 2024-07-10 18:43:58

大佬太好了,大佬一改就A了


|