二維線段樹【模板——給出對應註釋】
阿新 • • 發佈:2018-11-20
閒話少說,直接看註釋反而會更容易讀懂這段二維線段樹的模板:
#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);} } }