其他分享
首页 > 其他分享> > 二维线段树/树套树+标记永久化

二维线段树/树套树+标记永久化

作者:互联网

题目这里
二维线段树类似于一棵四叉树,四个子节点分别为left_up,left_down,right_up,right_down
其他与一维类似,要判断是否有这些儿子,常数大导致TLE
其实用数组可以不用build

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define R register
#define EN printf("\n")
#define LL long long
inline LL mmax(LL a,LL b){return a>b?a:b;}
inline LL read(){
    LL x=0,y=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') y=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+(c^48);c=getchar();}
    return x*y;
}
int n,m,k;
struct tr{
    tr *lup,*ldown,*rup,*rdown;
    LL max,changee;
}dizhi[2000006],*root=&dizhi[0];
int tot; 
void build(tr *tree,int l,int r,int u,int d){
    if(l==r&&u==d){tree->changee=tree->max=0;return;}
    tree->lup=&dizhi[++tot];
    tree->ldown=&dizhi[++tot];
    tree->rup=&dizhi[++tot];
    tree->rdown=&dizhi[++tot];
    int midlr=(l+r)>>1,midud=(u+d)>>1;
    build(tree->lup,l,midlr,u,midud);
    if(u!=d) build(tree->ldown,l,midlr,midud+1,d);
    if(l!=r){
        build(tree->rup,midlr+1,r,u,midud);
        if(u!=d) build(tree->rdown,midlr+1,r,midud+1,d);
    }
    tree->max=tree->changee=0;
}
inline void pushdown(tr *tree){
    if(!tree->changee) return;
    tree->lup->max=tree->changee;tree->lup->changee=tree->changee;
    tree->ldown->max=tree->changee;tree->ldown->changee=tree->changee;
    tree->rup->max=tree->changee;tree->rup->changee=tree->changee;
    tree->rdown->max=tree->changee;tree->rdown->changee=tree->changee;
    tree->changee=0;
}
void change(tr *tree,int l,int r,int u,int d,int ql,int qr,int qu,int qd,LL h){
    if(ql<=l&&r<=qr&&qu<=u&&d<=qd){
        tree->max=h;
        tree->changee=h;
        return;
    }
    pushdown(tree);
    int midlr=(l+r)>>1,midud=(u+d)>>1;
    if(ql<=midlr){
        if(qu<=midud) change(tree->lup,l,midlr,u,midud,ql,qr,qu,qd,h);
        if(qd>midud&&u!=d) change(tree->ldown,l,midlr,midud+1,d,ql,qr,qu,qd,h);
    } 
    if(qr>midlr&&l!=r){
        if(qu<=midud) change(tree->rup,midlr+1,r,u,midud,ql,qr,qu,qd,h);
        if(qd>midud&&u!=d) change(tree->rdown,midlr+1,r,midud+1,d,ql,qr,qu,qd,h);
    }
    tree->max=mmax(mmax(tree->lup->max,tree->ldown->max),mmax(tree->rup->max,tree->rdown->max));
}
LL q(tr *tree,int l,int r,int u,int d,int ql,int qr,int qu,int qd){
    if(ql<=l&&r<=qr&&qu<=u&&d<=qd) return tree->max;
    pushdown(tree);
    LL ret=-999999999999;
    int midlr=(l+r)>>1,midud=(u+d)>>1;
    if(ql<=midlr){
        if(qu<=midud) ret=std::max(ret,q(tree->lup,l,midlr,u,midud,ql,qr,qu,qd));
        if(qd>midud&&u!=d) ret=std::max(ret,q(tree->ldown,l,midlr,midud+1,d,ql,qr,qu,qd));
    }
    if(qr>midlr&&l!=r){
        if(qu<=midud) ret=std::max(ret,q(tree->rup,midlr+1,r,u,midud,ql,qr,qu,qd));
        if(qd>midud&&u!=d) ret=std::max(ret,q(tree->rdown,midlr+1,r,midud+1,d,ql,qr,qu,qd));
    }
    return ret;
}
int main(){
    n=read();m=read();k=read();
    build(root,1,n,1,m);
    int x,y,nn,mm;LL h;
    for(R int i=1;i<=k;i++){
        nn=read();mm=read();h=read();x=read();y=read();
        x++;y++;
        h+=q(root,1,n,1,m,x,x+nn-1,y,y+mm-1);
        change(root,1,n,1,m,x,x+nn-1,y,y+mm-1,h);
    }
    printf("%lld",root->max);
    return 0;
}

另一种方法是外层线段树维护x轴,每个节点对应两棵内层维护y轴的树(max树,tag树)
标记永久化:
max数组是区间最大值,tag为修改后的值
查询时:若完全包含某个区间,才用max更新(因为max是整个区间的最大值),否则,用tag(tag是对整个区间打的,所以子区间可以直接使用)
修改:完全包含,用k更新tag,否则用max
数组省去build后最大测试点679ms

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define R register
#define EN printf("\n")
#define LL long long
inline int read(){
    int x=0,y=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') y=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+(c^48);c=getchar();}
    return x*y;
}
inline int mmax(int a,int b){return a>b?a:b;}
int n,m,k;
struct segy{
    int max[4020],tag[4020];
    int q(int o,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr) return mmax(max[o],tag[o]);
        int mid=(l+r)>>1;
        int ret=-99999999;
        if(ql<=mid) ret=q(o<<1,l,mid,ql,qr);
        if(qr>mid) ret=mmax(ret,q(o<<1|1,mid+1,r,ql,qr));
        return mmax(ret,tag[o]);
    }
    void change(int o,int l,int r,int ql,int qr,int k){
        max[o]=mmax(max[o],k);
        if(ql<=l&&r<=qr){tag[o]=mmax(tag[o],k);return;}
        int mid=(l+r)>>1;
        if(ql<=mid) change(o<<1,l,mid,ql,qr,k);
        if(qr>mid) change(o<<1|1,mid+1,r,ql,qr,k);
    }
};
struct segx{
    segy tag[4020],max[4020];
    int q(int o,int l,int r,int ql,int qr,int u,int d){
        if(ql<=l&&r<=qr) return max[o].q(1,1,m,u,d);
        int mid=(l+r)>>1;
        int ret=-99999999;
        if(ql<=mid) ret=mmax(ret,q(o<<1,l,mid,ql,qr,u,d));
        if(qr>mid) ret=mmax(ret,q(o<<1|1,mid+1,r,ql,qr,u,d));
        return mmax(ret,tag[o].q(1,1,m,u,d));
    }
    void change(int o,int l,int r,int ql,int qr,int u,int d,int k){
        max[o].change(1,1,m,u,d,k);
        if(ql<=l&&r<=qr){tag[o].change(1,1,m,u,d,k);return;}
        int mid=(l+r)>>1;
        if(ql<=mid) change(o<<1,l,mid,ql,qr,u,d,k);
        if(qr>mid) change(o<<1|1,mid+1,r,ql,qr,u,d,k);
    }
}a;
int main(){
    n=read();m=read();k=read();
    int nn,mm,x,y,h;
    for(R int i=1;i<=k;i++){
        nn=read();mm=read();h=read();x=read()+1;y=read()+1;
        h+=a.q(1,1,n,x,x+nn-1,y,y+mm-1);
        a.change(1,1,n,x,x+nn-1,y,y+mm-1,h);
    }
    printf("%d",a.q(1,1,n,1,n,1,m));
    return 0;
}

标签:midlr,树套,int,max,线段,tree,永久化,midud,changee
来源: https://www.cnblogs.com/suxxsfe/p/12425368.html