树剖板子求调

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

galiyuebing @ 2023-11-06 20:27:08

rt,很迷的是dfs1,dfs2都没问题,查线段树也看不出来,主函数对着书也没啥问题(可能眼睛有问题) 但是样例就是过不了

debug了一下发现线段树更新有问题,但看了半小时也找不出,便求救万能的谷友

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#define pii pair<int,int>
#define ls k<<1
#define rs k<<1|1
using namespace std;
const int N=1e5+10;
long long mod,a[N],t[N*4],tag[N*4];
int siz[N],son[N],fa[N],id[N],top[N],dep[N],num;
int n,m,root;
int head[N*2],cnt;
struct Node{
    int to,next;
}edge[N*2];

void ini()
{
    for(int i=1;i<N*2;++i){head[i]=-1;edge[i].next=-1;}
}

void add_edge(int u,int v)
{
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

void dfs1(int u,int from)
{
    siz[u]=1;fa[u]=from;dep[u]=dep[from]+1;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v!=from)
        {
            dfs1(v,u);
            siz[u]+=siz[v];
            if(!son[u] || siz[son[u]]<siz[v])son[u]=v;
        }
    }
}

void dfs2(int u,int topx)
{
    id[u]=++num;
    top[u]=topx;
    if(!son[u])return;
    dfs2(son[u],topx);
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v!=fa[u] && v!=son[u])dfs2(v,v);
    }
}

void update(int k){t[k]=t[ls]+t[rs];}

void push_down(int k,int l,int r)
{
    if(!tag[k])return;
    int mid=(l+r)>>1;
    tag[ls]+=tag[k];t[ls]+=(mid-l+1)*tag[k];
    tag[rs]+=tag[k];t[rs]+=(r-mid)*tag[k];
    tag[k]=0;
    return;
}

void build(int k,int l,int r)
{
    if(l==r)
    {
        t[k]=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);build(rs,mid+1,r);
    update(k);
    return;
}

void Add(int k,int l,int r,int L,int R,long long s)
{
    if(l>=L && r<=R)
    {
        tag[k]+=s;
        t[k]+=(r-l+1)*s;
        return;
    }
    int mid=(l+r)>>1;
    if(R<=mid)Add(ls,l,mid,L,R,s);
    else if(L>mid)Add(rs,mid+1,r,L,R,s);
    else {Add(ls,l,mid,L,R,s);Add(rs,mid+1,r,L,R,s);}
    update(k);
}

long long query(int k,int l,int r,int L,int R)
{
    push_down(k,l,r);
    if(l>=L && r<=R)return t[k];
    int mid=(l+r)>>1;
    if(R<=mid)return query(ls,l,mid,L,R);
    else if(L>mid)return query(rs,mid+1,r,L,R);
    else return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
}

int main()
{ini();
    cin>>n>>m>>root>>mod;
    for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
    for(int i=1,u,v;i<n;++i)
    {
        scanf("%d%d",&u,&v);
        add_edge(u,v);add_edge(v,u);
    }
    dfs1(root,0);
    dfs2(root,root);
    build(1,1,n);
    for(int i=1,opt;i<=m;++i)
    {
        scanf("%d",&opt);
        if(opt==1)
        {
            int x,y;long long z;
            scanf("%d%d%lld",&x,&y,&z);
            while(top[x]!=top[y])
            {
                if(dep[top[x]]<dep[top[y]])swap(x,y);
                Add(1,1,n,id[top[x]],id[x],z);
                x=fa[top[x]];
            }
            if(dep[x]<dep[y])swap(x,y);
            Add(1,1,n,id[y],id[x],z);

        }
        else if(opt==2)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            long long ans=0;
            while(top[x]!=top[y])
            {
                if(dep[top[x]]<dep[top[y]])swap(x,y);
                ans=(ans+query(1,1,n,id[top[x]],id[x]))%mod;
                x=fa[top[x]];
            }
            if(dep[x]<dep[y])swap(x,y);
            ans=(ans+query(1,1,n,id[y],id[x]))%mod;
            printf("%lld\n",ans%mod);
        }
        else if(opt==3)
        {
            int x;long long z;
            scanf("%d%lld",&x,&z);
            Add(1,1,n,id[x],id[x]+siz[x]-1,z);
        }
        else
        {
            int x;
            scanf("%d",&x);
            printf("%lld\n",query(1,1,n,id[x],id[x]+siz[x]-1)%mod);
        }
    }
    return 0;
}

by Fasfree @ 2023-11-06 20:40:39

线段树进左右儿子的条件好像不是if 和else if 的关系


by zhzkiller @ 2023-11-06 20:42:42

@galiyuebing 而且你没有反馈数组,你只有节点对应线段上的点没有线段上的点对应节点那个数组


by galiyuebing @ 2023-11-06 20:43:53

@Fasfree 可能是我入门的时候老师教的原因

int mid=(l+r)>>1;
if(R<=mid) ... //左儿子
else if(L>mid) ...//右儿子 
else ...,...//mid在[L,R]中间,左右儿子都遍历

if(L<=mid) ...//左儿子
if(R>mid)... //右儿子

这两种应该是等价的


by zhzkiller @ 2023-11-06 20:45:54

@galiyuebing 进分支的顺序反了


by galiyuebing @ 2023-11-06 20:46:11

@zhzkiller ??什么是线段上的点对应节点的数组? 这个代码有关线段树的数组是a[N],t[N4],tag[N4]

a是原序列的数

t是区间和

tag是懒标记


by zhzkiller @ 2023-11-06 20:46:30

先判是不是中间,再左右儿子


by Fasfree @ 2023-11-06 20:47:18

@galiyuebing 这题不用那东西


by zhzkiller @ 2023-11-06 20:47:24

@galiyuebing 就是你线段树上建树的时候,你实际对应线段树上端点应该在树上的对应值,而不是本身。

你想想,树可不是一条链


by galiyuebing @ 2023-11-06 20:48:15

@zhzkiller 先判断是不是[L,R]都在[L,mid]中,再是[mid+1,R]中,如果都不是,就说明mid∈[L,R]


by Fasfree @ 2023-11-06 20:48:28

@zhzkiller 他的写法是先判是不是全在左儿子,然后判是不是全在右儿子,都不是就两个都进


上一页 | 下一页