二维线段树->树套树

  现在上真正的二维线段树 毕竟 刚刚那个是卡常 过题我们现在做一个更高级的做法二维线段树。

大体上维护一颗x轴线段树 然后在每个节点的下方再吊一颗维护y轴的线段树那么此时我们整个平面就被我们玩好了。

这样形成二维线段树比刚才的要 合理多了。

写起来 不免有点蒙蔽...然后突然就顿悟了 其实每次我们对于区间的修改大概就是先把x属于x轴的那一段区间给拎出来然后在那个区间之中把那个区间的y轴线段树给修改掉。

还是刚刚那道题 这次是MLE 了 理论上二维线段树空间复杂度 (MAXN<<2)^2这个复杂度 可是正中下怀的MLE了 开小了一点就可以过了。

//#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;
}
View Code

操作还是不太熟悉 觉得这个可持久化有点神仙没写过有点 怀疑 不过正确性 和 合理性都是比较显然的。

简述一下大体的思路吧 首先对x轴开一棵线段树然后 在x轴的这颗线段树每个节点处都吊着一棵y轴的线段树。

下传修改标记 的时候 对于到过路径上的所有的节点都应该修改值 因为这是标记所致 到了锁定的区间之后标记永久化 对这个tag打上标记 而对内部的y轴线段树也进行一次标记永久化。

通俗的来说 内外线段树处理方式一模一样。为什么不能下传标记 显然的是我们对于外层线段树的修改 在自己的内部是无法下传的 因为标记是无法合并的 比如说 ta 要修改1 n 1 2这个区间ta又要修改1 n 1 3这个区间显然这两个区间在x线段树上是对等的在y线段树上可并不对等所以这两个标记很难合并 故应该标记永久化减少合并的麻烦。

这里我们很完美的解决了 二维线段树的问题。 单次操作时间显然是 logn^2.

posted @ 2019-08-16 15:43  chdy  阅读(1231)  评论(0编辑  收藏  举报