树剖37pts求助

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

YGW6 @ 2024-07-10 15:44:04

蒟蒻刚学树剖,麻烦各位dalao帮助一下QAQ

代码37pts,过样例了,#1,#2,#3,#11过了,其余的WA

代码有部分注释

#include<iostream>
#include<cmath>
#include<string>
#include<string.h>
#include<algorithm>

using namespace std;

const int N=1e5+5;

int n,m,r,mod;
int answer;
int w[N];
int cnt;
int fa[N],dep[N],size[N],son[N],top[N],id[N],wt[N];
/*
fa[u]:u在树中的父亲
dep[u]:u节点的深度
size[u]:u的子树节点数(子树大小)
son[u]:u的重儿子
top[u]:u所在重链的顶部节点
id[x]:新的编号
wt[y]:新编号的点权 
*/

struct edge
{
    int u,v;
}e[N*2];
int h[N*2],tot;

int sum[N*4],lazy[N*4];

void add(int u,int v)
{
    e[++tot].v=v;
    e[tot].u=h[u];
    h[u]=tot;
}

void dfs1(int u,int f,int deep)
{
    dep[u]=deep;    //标记深度 
    fa[u]=f;        //标记节点的父亲 
    size[u]=1;      //记录每个节点子树大小 
    int maxson=-1;  //记录重儿子数量 
    for(int i=h[u];i!=-1;i=e[i].u)
    {
        int v=e[i].v;
        if(v==f)
            continue;
        dfs1(v,u,deep+1);
        size[u]+=size[v];
        if(size[v]>maxson)  //儿子里最多 size 就是重儿子 
        {
            son[u]=v;  //标记u的重儿子为 v 
            maxson=size[v];
        }
    }
}

//处理出top[],wt[],id[] 
void dfs2(int u,int topf)
{
    id[u]=++cnt;   //每个节点的新编号 
    wt[cnt]=w[u];  //新编号的对应权值 
    top[u]=topf;   //标记每个重链的顶端 
    if(!son[u])    //没有儿子时返回 
        return ;
    dfs2(son[u],topf);  //搜索下一个重儿子 
    for(int i=h[u];i!=-1;i=e[i].u)
    {
        int v=e[i].v;
        if(v==fa[u] or v==son[u])  //处理轻儿子 
            continue;
        dfs2(v,v);  //每一个轻儿子都有一个从自己开始的链 
    }
}

void push_up(int u)
{
    sum[u]=(sum[2*u]+sum[2*u+1])%mod;
}

void push_down(int u,int len)  //下放lazy标记 
{
    lazy[2*u]+=lazy[u];
    lazy[2*u+1]+=lazy[u];
    sum[2*u]+=lazy[u]*(len-len/2);
    sum[2*u]%=mod;
    sum[2*u+1]+=lazy[u]*len/2;
    sum[2*u+1]%=mod;
    lazy[u]=0;
}

void build(int u,int l,int r)
{
    lazy[u]=0;
    if(l==r)
    {
        sum[u]=wt[l];  //新的编号点权 
        sum[u]%=mod;
        return ;
    }
    int mid=(l+r)/2;
    build(2*u,l,mid);
    build(2*u+1,mid+1,r);
    push_up(u);
}

void update(int L,int R,int c,int l,int r,int u)
{
    if(L<=l&&R>=r)
    {
        lazy[u]+=c;
        sum[u]+=c*(r-l+1);
        sum[u]%=mod;
        return ;
    }
    if(lazy[u])
        push_down(u,r-l+1);
    int mid=(l+r)/2;
    if(L<=mid)
        update(L,R,c,l,mid,2*u);
    if(R>mid)
        update(L,R,c,mid+1,r,2*u+1);
    push_up(u);
}

void updrange(int x,int y,int k)
{
    k%=mod;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])  //使x深度较大 
            swap(x,y);
        update(id[top[x]],id[x],k,1,n,1);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])  //使x深度较小 
        swap(x,y);
    update(id[x],id[y],k,1,n,1);
}

void query(int L,int R,int l,int r,int u)
{
    if(L<=l&&R>=r)
    {
        answer+=sum[u];
        answer%=mod;
        return ;
    }
    if(lazy[u])
        push_down(u,r-l+1);
    int mid=(l+r)/2;
    if(L<=mid)
        query(L,R,l,mid,2*u);
    if(R>mid)
        query(L,R,mid+1,r,2*u+1);
}

int qrange(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])  //当两个点不在同一条链上 
    {
        if(dep[top[x]]<dep[top[y]])  //使x深度较大 
            swap(x,y);
        answer=0;
        query(id[top[x]],id[x],1,n,1);  //ans加上x点到x所在链顶端这一段区间的点权和 
        ans+=answer;
        ans%=mod;
        x=fa[top[x]];  //x跳到x所在链顶端的这个点的上面一个点 
    }
    //当两个点处于同一条链 
    if(dep[x]>dep[y])  //使x深度较小 
        swap(x,y);
    answer=0;
    query(id[x],id[y],1,n,1);
    ans+=answer;
    ans%=mod;
    return ans;
}

void upson(int x,int k)
{
    update(id[x],id[x]+size[x]-1,k,1,n,1);  //子树区间右端点为id[x]+siz[x]-1 
}

int qson(int x)
{
    answer=0;
    query(id[x],id[x]+size[x]-1,1,n,1);
    return answer;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>r>>mod;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++)
        cin>>w[i];
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    dfs1(r,0,1);  //根节点,父亲,深度 
    dfs2(r,r);
    build(1,1,n);  //用新点权建立线段树 
    while(m--)
    {
        int op,x,y,z;
        cin>>op;
        if(op==1)
        {
            cin>>x>>y>>z;
            updrange(x,y,z);
        }
        else if(op==2)
        {
            cin>>x>>y;
            cout<<qrange(x,y)<<endl;
        }
        else if(op==3)
        {
            cin>>x>>z;
            upson(x,z);
        }
        else if(op==4)
        {
            cin>>x;
            cout<<qson(x)<<endl;
        }
    }
    return 0;
}

by xuhaotian @ 2024-07-10 16:13:09

@YGW6 push_down出问题了


by xuhaotian @ 2024-07-10 16:13:55

算左右儿子的区间长度那块错了


by YGW6 @ 2024-07-10 16:20:41

@xuhaotian

dalao麻烦再问一下哪里错了?没看出来啊


by xuhaotian @ 2024-07-10 16:26:50

应该用左右区间端点算长度,直接用父亲的len/2是假的


by xuhaotian @ 2024-07-10 16:27:52

@YGW6

cpp
void push_down(int u,int l,int r)  //下放lazy标记 
{
    lazy[2*u]+=lazy[u];
    lazy[2*u+1]+=lazy[u];
    int mid=l+r/2;
    sum[2*u]+=lazy[u]*(mid-l+1);
    sum[2*u]%=mod;
    sum[2*u+1]+=lazy[u]*(r-mid);
    sum[2*u+1]%=mod;
    lazy[u]=0;
}

改成这样就可以了


by YGW6 @ 2024-07-10 16:34:55

@xuhaotian

谢谢dalao,麻烦了,以关注


by YGW6 @ 2024-07-10 16:36:06

不过那个len/2算出来是会比正常的值少0.5是吗?


by chenzhiyv @ 2024-07-10 16:39:36

你代码中,len=r-l+1,并且你在算左儿子时直接用len-len/2,将len=r-l+1带入,可以算出左儿子的区间为0.5r-0.5l+0.5,但正确的左儿子区间应为mid-l,mid=(l+r)/2 带入算出有正确的左儿子区间是0.5r-0.5l+1,所以错了,右儿子同理


by chenzhiyv @ 2024-07-10 16:40:12

但我不确定加上0.5会不会对


by YGW6 @ 2024-07-10 16:44:53

@chenzhiyv

行,谢谢你


|