1. 程式人生 > >二維線段樹【模板——給出對應註釋】

二維線段樹【模板——給出對應註釋】

閒話少說,直接看註釋反而會更容易讀懂這段二維線段樹的模板:


#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);}
    }
}