蒟蒻求助,全WA,关于思路有自己的注释,根《深入浅出》学的

P3372 【模板】线段树 1

ysq20110325 @ 2023-07-17 20:51:52

#include<bits/stdc++.h>
using namespace std;
int n,m;
long long a[1111111],w[4111111],lzy[4111111];
//n—点的个数
//m—边的个数
//a—单个点的点值(数组) 
//w—区间和(数组)
//lzy—延迟标记(数组) 
//赋值操作 
void pushup(const int u){
    //u为要赋值的结点 
    w[u]=w[u*2]+w[u*2+1];//u*2是父节点u的左子树,u*2+1是其的右子树 
    return;
}
//递归建造线段树 
void build(const int u,int l,int r){
    //u—当前结点,l—其左子结点,r—其右子结点 
    if(l==r){//没有子结点,即点u为叶子结点 
        w[u]=a[l];//赋值 
        return;
    }
    int mid=(l+r)/2;//将区间分为左右子区间
    build(u*2,l,mid);//递归建立其左子树
    build(u*2+1,mid+1,r);//递归建立其右子树
    pushup(u);//由其左右子区间更新该区间的和 
    return;
}
//单点修改操作
void update1(int u,int l,int r,int p,long long x){
    //u—当前结点,l—其左子结点,r—其右子结点 p—要修改的结点 x—要修改的值 
    if(l==r){//到达叶节点直接赋值 
        w[u]=x;
    }else {
        int mid=(l+r)/2;//将区间分为左右子区间
        if(mid>=p){//如果要修改的位置在左子树的区间,修改左子树 
            update1(u*2,l,mid,p,x);
        }else{
            update1(u*2+1,mid+1,r,p,x);//否则修改右子树的区间 
        }
        pushup(u);//更新当前节点的值 
    }
    return;
}
//单点查询操作 
long long query1(int u,int l,int r,int p){
    //u—当前结点,l—其左子结点,r—其右子结点 p—要查询的结点 
    if(l==r){//抵达叶结点就返回 
        return w[u];
    }else {
        int mid=(l+r)/2;//将区间分为左右子区间
        if(mid>=p){
            return query1(u*2,l,mid,p);//如果要查询的位置在左子树的区间,就查询左子树的区间 
        }else{
            return query1(u*2+1,mid+1,r,p);//反之查询右子树的区间 
        }
    }
}
//判断一个子区间是否被另一个完全包含 
bool inrange(int l1,int r1,int l2,int r2){
    //l1—一个子区间的左端点 r1—一个子区间的右端点 l2—另一个子区间的左端点 r2—另一个子区间的右端点
    return(l2<=l1)&&(r1<=r2);//判断[l1,r1]是否被[l2,r2]完全包含 
}
//判断一个子区间是否完全与另一个无交 
bool outofrange(int l1,int r1,int l2,int r2){
    //l1—一个子区间的左端点 r1—一个子区间的右端点 l2—另一个子区间的左端点 r2—另一个子区间的右端点
    return (l1>r2)||(r1<l2);//判断[l1,r1]是否与[l2,r2]完全无交 
}
//区间查询操作 
long long query2(int u,int l1,int r1,int l2,int r2){
    //u—当前结点,l1—其左子结点,r1—其右子结点 l2—要查询的区间的左端点 r2—要查询的区间的右端点 
    if(inrange(l1,r1,l2,r2)){//如果被完全包含就返回该区间区间和
        return w[u];
    }else if(!outofrange(l1,r1,l2,r2)){//如果有区间局部被包含了 
        int mid=(l1+r1)/2;//将区间分为左右子区间
        return query2(u*2,l1,mid,l2,r2)+query2(u*2+1,mid+1,r1,l2,r2);//返回左右子区间中包含目标区间中的元素的值的和 
    }else return 0;//否则一定完全无交 
}
//放置延迟标记    
void maketag(int u,int len,long long x){
    //u—要放置延迟标记的结点 len—要在u结点的子区间内放置延迟标记的结点个数 x—要放置的延迟标记的值 
    lzy[u]+=x;//修改当前结点的延迟标记 
    w[u]+=len*x;//修改当前结点的区间和 
    return;
}
//把延迟标记放置到下一层 
void pushdown(int u,int l,int r){
    //u—当前结点,l—其左子结点,r—其右子结点
    int mid=(l+r)/2;//将区间分为左右子区间
    maketag(u*2,mid-l+1,lzy[u]);//为u结点的左子树加上延迟标记 
    maketag(u*2+1,r-mid,lzy[u]);//为u结点的右子树加上延迟标记
    lzy[u]=0;//延迟标记以传至下一层,清空当前层的延迟标记
    return;
}
//区间修改 
void update2(int u,int l1,int r1,int l2,int r2,long long x){
    //l1—一个子区间的左端点 r1—一个子区间的右端点 l2—要修改的子区间的左端点 r2—要修改的子区间的右端点 x—要修改的值 
    if(inrange(l1,r1,l2,r2)){//如果完全被包含
        maketag(u,r1-l1+1,x);//直接给该区间打上延迟标记 
    }else if(!outofrange(l1,r1,l2,r2)){
        int mid=(l1+r1)/2;//将区间分为左右子区间
        pushdown(u,l1,r1);//下传延迟标记 
        update2(u*2,l1,mid,l2,r2,x);//递归修改以为u结点的左子树
        update2(u*2+1,mid+1,r1,l2,r2,x);//递归修改以为u结点的右子树
        pushup(u);//修改u
    }
    return;
} 
int main(){ 
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    build(1,1,n);//建立线段树 
    for(int t=1;t<=m;t++){
        int op,x,y;
        long long k=0;
        cin>>op;//表示操作(修改/查询) 
        if(op==1){//如果要修改区间 
            //x—要修改的子区间的左端点 y—要修改的子区间的右端点 k—增加的值 
            cin>>x>>y>>k;
            update2(1,1,n,x,y,k);
        }else {//否则查询区间和
            //x—要查询的区间的左端点 y—要查询的区间的右端点 
            cin>>x>>y;
            cout<<query2(1,1,n,x,y)<<endl;
        }
    }
    return 0;
}

by ysq20110325 @ 2023-07-17 20:53:54

一组hack: 8 10 640 591 141 307 942 58 775 133 2 1 5 2 3 8 2 3 6 2 5 8 2 4 8 1 4 8 60 2 1 6 2 5 8 1 3 7 15 1 2 6 86


by ysq20110325 @ 2023-07-17 20:55:35

倒数第二次询问期望2859,输出2739


by ysq20110325 @ 2023-07-17 20:56:34

完整答案应为:2621 2356 1448 1908 2215 2859 2148


by xun_xun @ 2023-07-18 06:49:49

区间查询要pushdown吧


by xun_xun @ 2023-07-18 06:50:17

@ysq20110325


by xun_xun @ 2023-07-18 06:56:04

懒标记原理是要用的时候用,而查询正是要用的时候,我认为查询要写在pushdown下面,在调用自己之前先将当前结点进行pushdown操作

如我的代码:


long long ask(int l,int r,int rt)
{
    if(tr[rt].l>r or tr[rt].r<l) return 0;
    if(tr[rt].l>=l and tr[rt].r<=r) return tr[rt].sum;
    pushdown(rt);
    return ask(l,r,rt*2)+ask(l,r,rt*2+1);
}

by xun_xun @ 2023-07-18 07:02:52

我手动修改后可以过楼上的数据,提交能不能AC我不知道


by xun_xun @ 2023-07-18 07:06:08

楼主代码区间查询部分(已删除注释):


long long query2(int u,int l1,int r1,int l2,int r2){
    if(inrange(l1,r1,l2,r2)){
        return w[u];
    }else if(!outofrange(l1,r1,l2,r2)){
        int mid=(l1+r1)/2;
        pushdown(u,l1,r1);
        return query2(u*2,l1,mid,l2,r2)+query2(u*2+1,mid+1,r1,l2,r2);
    }else return 0;
}

by ysq20110325 @ 2023-07-18 08:48:49

@xun_xun 以能通过,谢谢大佬


|