二维线段树->树套树
现在上真正的二维线段树 毕竟 刚刚那个是卡常 过题我们现在做一个更高级的做法二维线段树。
大体上维护一颗x轴线段树 然后在每个节点的下方再吊一颗维护y轴的线段树那么此时我们整个平面就被我们玩好了。
这样形成二维线段树比刚才的要 合理多了。
写起来 不免有点蒙蔽...然后突然就顿悟了 其实每次我们对于区间的修改大概就是先把x属于x轴的那一段区间给拎出来然后在那个区间之中把那个区间的y轴线段树给修改掉。
还是刚刚那道题 这次是MLE 了 理论上二维线段树空间复杂度 (MAXN<<2)^2这个复杂度 可是正中下怀的MLE了 开小了一点就可以过了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<ctime> #include<cstring> #include<string> #include<ctime> #include<cctype> #include<cstdio> #include<utility> #include<queue> #include<stack> #include<deque> #include<map> #include<set> #include<bitset> #include<vector> #include<algorithm> #include<cstdlib> #define INF 1000000000 #define ll long long #define mx(p) t[p].mx #define tag(p) t[p].tag #define RE register using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { RE int x=0,f=1;RE char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=3000; int n,m,Q; int qx,xx,qy,yy,h; inline int max(int x,int y){return x>y?x:y;} struct wy { int mx[MAXN],tag[MAXN]; inline void change(int p,int l,int r,int x,int y,int k) { mx[p]=max(mx[p],k); if(l==x&&r==y) { tag[p]=max(tag[p],k); return; } int mid=(l+r)>>1; if(y<=mid)change(p<<1,l,mid,x,y,k); else { if(x>mid)change(p<<1|1,mid+1,r,x,y,k); else change(p<<1,l,mid,x,mid,k),change(p<<1|1,mid+1,r,mid+1,y,k); } } inline int ask(int p,int l,int r,int x,int y) { if(l==x&&r==y)return mx[p]; int mid=(l+r)>>1,ans=tag[p]; if(y<=mid)ans=max(ans,ask(p<<1,l,mid,x,y)); else { if(x>mid)ans=max(ans,ask(p<<1|1,mid+1,r,x,y)); else ans=max(ans,max(ask(p<<1,l,mid,x,mid),ask(p<<1|1,mid+1,r,mid+1,y))); } return ans; } }; struct tx { wy mx[MAXN],tag[MAXN]; inline void change(int p,int l,int r,int x,int y,int k) { mx[p].change(1,1,m,qy,yy,k); if(l==x&&r==y) { tag[p].change(1,1,m,qy,yy,k); return; } int mid=(l+r)>>1; if(y<=mid)change(p<<1,l,mid,x,y,k); else { if(x>mid)change(p<<1|1,mid+1,r,x,y,k); else change(p<<1,l,mid,x,mid,k),change(p<<1|1,mid+1,r,mid+1,y,k); } } inline int ask(int p,int l,int r,int x,int y) { if(l==x&&r==y)return mx[p].ask(1,1,m,qy,yy); int mid=(l+r)>>1,ans=tag[p].ask(1,1,m,qy,yy); if(y<=mid)ans=max(ans,ask(p<<1,l,mid,x,y)); else { if(x>mid)ans=max(ans,ask(p<<1|1,mid+1,r,x,y)); else { ans=max(ans,ask(p<<1,l,mid,x,mid)); ans=max(ans,ask(p<<1|1,mid+1,r,mid+1,y)); } } return ans; } }T; int main() { freopen("1.in","r",stdin); n=read();m=read();Q=read(); while(Q--) { xx=read();yy=read();h=read(); qx=read()+1;qy=read()+1; xx=qx+xx-1;yy=qy+yy-1; int ans=T.ask(1,1,n,qx,xx); T.change(1,1,n,qx,xx,ans+h); } qx=1;qy=1;xx=n;yy=m; printf("%d\n",T.ask(1,1,n,qx,xx)); return 0; }
操作还是不太熟悉 觉得这个可持久化有点神仙没写过有点 怀疑 不过正确性 和 合理性都是比较显然的。
简述一下大体的思路吧 首先对x轴开一棵线段树然后 在x轴的这颗线段树每个节点处都吊着一棵y轴的线段树。
下传修改标记 的时候 对于到过路径上的所有的节点都应该修改值 因为这是标记所致 到了锁定的区间之后标记永久化 对这个tag打上标记 而对内部的y轴线段树也进行一次标记永久化。
通俗的来说 内外线段树处理方式一模一样。为什么不能下传标记 显然的是我们对于外层线段树的修改 在自己的内部是无法下传的 因为标记是无法合并的 比如说 ta 要修改1 n 1 2这个区间ta又要修改1 n 1 3这个区间显然这两个区间在x线段树上是对等的在y线段树上可并不对等所以这两个标记很难合并 故应该标记永久化减少合并的麻烦。
这里我们很完美的解决了 二维线段树的问题。 单次操作时间显然是 logn^2.