二维线段树【模板——给出对应注释】

闲话少说,直接看注释反而会更容易读懂这段二维线段树的模板:


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define fur(i,x,y) for(i=x;i<=y;i++)
#define fdr(i,x,y) for(i=x;i>=y;i--)
#define Fur(i,x,y) for(ll i=x;i<=y;i++)
#define Fdr(x,y) for(ll i=x;i>=y;i--)
#define in2(x,y) in(x);in(y)
#define in3(x,y,z) in2(x,y);in(z)
#define in4(a,b,c,d) in2(a,b);in2(c,d)
#define clr(x,y) memset(x,y,sizeof(x))
#define cpy(x,y) memcpy(x,y,sizeof(x))
typedef long long ll;
using namespace std;
namespace fib{char b[300000]= {},*f=b;}
#define gc ((*fib::f)?(*(fib ::f++)):(fgets(fib::b,sizeof(fib::b),stdin)?(fib::f=fib::b,*(fib::f++)):-1))
inline void in(ll &x){x=0;char c;bool f=0;while((c=gc)>'9'||c<'0')if(c=='-')f=!f;x=c-48;while((c=gc)<='9'&&c>='0')x=x*10+c-48;if(f)x=-x;}
namespace fob{char b[300000]= {},*f=b,*g=b+300000-2;}
#define pob (fwrite(fob::b,sizeof(char),fob::f-fob::b,stdout),fob::f=fob::b,0)
#define pc(x) (*(fob::f++)=(x),(fob::f==fob::g)?pob:0)
struct foce{~foce(){pob;fflush(stdout);}} _foce;
namespace ib{char b[100];}
inline void out(ll x){if(x==0){pc(48);return;}if(x<0){pc('-');x=-x;}char *s=ib::b;while(x) { *(++s)=x%10;x/=10; } while(s!=ib::b) pc((*(s--))+48);}
inline void outn(ll x){out(x);pc('\n');}
inline ll jdz(ll x){return x>0?x:-x;}
#define N 305
#define c1 (rt*4-2)     //左上子节点
#define c2 (rt*4-1)     //右上子节点
#define c3 (rt*4)       //左下子节点
#define c4 (rt*4+1)     //右下子节点
#define z1 x1,y1,mx,my
#define z2 x1,my+1,mx,y2
#define z3 mx+1,y1,x2,my
#define z4 mx+1,my+1,x2,y2
#define Z ll mx=(x1+x2)>>1,my=(y1+y2)>>1
#define U ll x1,ll y1,ll x2,ll y2,ll rt
#define pu s[rt]=s[c1]+s[c2]+s[c3]+s[c4]    //树的更新
ll s[N*N*4],laz[N*N*4],a[N][N],n,m,q,g[N*N*4];
bool b[N*N*4];
inline ll gs(ll x1,ll y1,ll x2,ll y2){return (jdz(x1-x2)+1)*(jdz(y1-y2)+1);}
inline void pd(ll rt,ll n1,ll n2,ll n3,ll n4)   //向下pushdown,n为其中的面积
{
    if(laz[rt])
    {
        ll t=laz[rt];
        s[c1]+=n1*t;
        s[c2]+=n2*t;
        s[c3]+=n3*t;
        s[c4]+=n4*t;
        laz[c1]+=t;
        laz[c2]+=t;
        laz[c3]+=t;
        laz[c4]+=t;
        laz[rt] = 0;
    }
}
inline void build(U)    //建树
{
    g[rt]=gs(x1,y1,x2,y2);  //面积
    if(x1==x2&&y1==y2){s[rt]=a[x1][y1]; return;}    //到底了,叶子结点的返回
    Z;
    build(z1,c1);   //左上节点是一直可以走的,跟求到的mid有关,所以左上是一定可以走的
    if(y1!=y2)build(z2,c2); //右上角的处理方式
    else b[c2]=1;       //右上角到底了
    if(x1!=x2) build(z3,c3);    //左下角是否可以建树
    else b[c3]=1;   //左下角到底了
    if(x1!=x2 && y1!=y2)build(z4,c4);   //此时才可以建立右下角(条件最为繁多)
    else b[c4]=1;   //右下角建立不了了
    pu;     //pushup()返回更新
}
inline void upd(ll X1,ll Y1,ll X2,ll Y2,ll v,U) //更新update()
{
    if(b[rt]) return;   //到叶子结点了,必须得返回
    if(X1<=x1&&Y1<=y1&&x2<=X2&&y2<=Y2){ s[rt]+=gs(x1,y1,x2,y2)*v; laz[rt]+=v; return; } //恰好是需要的区间,就直接返回即可
    Z;      //mid_x、mid_y的处理
    pd(rt,g[c1],g[c2],g[c3],g[c4]); //pushdown的是lazy标记
    if(X1<=mx&&Y1<=my)upd(X1,Y1,X2,Y2,v,z1,c1); //更新区间在左上角
    if(X1<=mx&&Y2>my)upd(X1,Y1,X2,Y2,v,z2,c2);  //更新区间在右上角
    if(X2>mx&&Y1<=my)upd(X1,Y1,X2,Y2,v,z3,c3);  //更新区间在左下角
    if(X2>mx&&Y2>my)upd(X1,Y1,X2,Y2,v,z4,c4);   //更新区间在右下角
    pu; //向上更新
}
inline ll qh(ll X1,ll Y1,ll X2,ll Y2,U) //查询函数
{
    if(b[rt]) return 0; //查到叶子节点了
    if(X1<=x1 && Y1<=y1 && x2<=X2 && y2<=Y2) return s[rt];
    Z,ans=0;    //初始化ans,列写mid_x, mid_y
    pd(rt,g[c1],g[c2],g[c3],g[c4]); //pushdown()lazy标记
    if(X1<=mx&&Y1<=my) ans+=qh(X1,Y1,X2,Y2,z1,c1);
    if(X1<=mx&&Y2>my) ans+=qh(X1,Y1,X2,Y2,z2,c2);
    if(X2>mx&&Y1<=my) ans+=qh(X1,Y1,X2,Y2,z3,c3);
    if(X2>mx&&Y2>my) ans+=qh(X1,Y1,X2,Y2,z4,c4);
    return ans;
}
int main(){
    in3(n,m,q);
    Fur(i,1,n)Fur(j,1,m)in(a[i][j]);
    build(1,1,n,m,1);
    ll p,x1,y1,x2,y2,v;
    while(q--){
        in(p);in4(x1,y1,x2,y2);
        if(p==1)outn(qh(x1,y1,x2,y2,1,1,n,m,1));
        else{in(v);upd(x1,y1,x2,y2,v,1,1,n,m,1);}
    }
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值