[POI2006]TET-Tetris 3D

传送门

要做这道题我们需要两个前置技能:二维线段树和标记永久化。

我们使用一维线段树来维护一个序列,那我们想维护一个矩阵的时候,二维线段树应运而生。

二维线段树好像有两种实现方法。一是对于每一个节点(x轴上的每个点)在里面再开一棵线段树(表示一个y轴)(这好像更多人管他叫树套树做法?)

第二个是把它变成一棵四叉树……不过这种实现方法我并没有学。

具体的实现方法其实非常优秀,可以选择写结构体。就是对于一次修改,正常在我们一维线段树上传6个参数,我们再多传两个记录y轴修改范围的参数,然后在到达制定修改位置的时候我们进入y轴去修改即可。(这个可以一会看代码)

之后再说说标记永久化,我们在进行区间修改的时候是要pushdown的,但是这个对于树套树等等比较复杂的数据结构是很麻烦的,而且尤其是像这道题,你不知道怎么下放。所以我们进行标记永久化。以区间加法为例,我们维护sum和add两个标记,在修改到指定位置之前,每经过一个区间就把他的sum加上加的值*区间长度。然后在修改到指定位置(完全覆盖的时候)我们把这个区间的add加上加的值。

然后在每次之后的查询中,我们每次向下走,都要加上这个区间所贡献的add*区间长度,这样才是结果(注意区间完全覆盖的时候不加,因为这个已经算在sum里面了)

那我们现在来看这道题。题目翻译的太好了,特别简练,而且是直接告诉你要干啥而不是让你再总结一遍。

我们用二维线段树维护这个序列。对于每次修改,我们先查询这个矩阵内的最大值,然后直接改成最大值+h的高度就行。

在途中是可以标记永久化的。这个思路和区间加法是一样的,不过这个变成了永久取最大值,所以在每次下放的时候更新当前mx(最大值),直到区间完全覆盖更新tag(修改后最大值),在query的时候对于一个区间,所有query的值都要与这个区间的tag去比较,然后返回较大值。

看一下代码。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('
')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 100005;
const int N = 8005;
 
int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >='0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

int d,s,h,x,y,k,n,m;

struct segy//维护y轴
{
    int mx[N],tag[N];
    void change(int p,int l,int r,int kl,int kr,int val)
    {
        mx[p] = max(mx[p],val);//更新(标记永久化)
        if(l == kl && r == kr)
        {
        tag[p] = max(tag[p],val);//区间完全覆盖的时候更新
        return;
        }
        int mid = (l+r) >> 1;//一切正常
        if(kr <= mid) change(p<<1,l,mid,kl,kr,val);
        else if(kl > mid) change(p<<1|1,mid+1,r,kl,kr,val);
        else change(p<<1,l,mid,kl,mid,val),change(p<<1|1,mid+1,r,mid+1,kr,val);
    }
    int query(int p,int l,int r,int kl,int kr)
    {
        if(l == kl && r == kr) return mx[p];//完全覆盖
        int cur = tag[p],mid = (l+r) >> 1;//剩下的所有与tag比较
        if(kr <= mid) cur = max(query(p<<1,l,mid,kl,kr),cur);
        else if(kl > mid) cur = max(query(p<<1|1,mid+1,r,kl,kr),cur);
        else cur = max(cur,max(query(p<<1,l,mid,kl,mid),query(p<<1|1,mid+1,r,mid+1,kr)));
        return cur;
    }
};

struct segx//维护x轴
{
    segy mx[N],tag[N];
    void change(int p,int l,int r,int kl,int kr,int zl,int zr,int val)
    {
        mx[p].change(1,1,m,zl,zr,val);//每次都修改(标记永久化)
        if(l == kl && r == kr)
        {
        tag[p].change(1,1,m,zl,zr,val);//只有完全覆盖才修改
        return;
        }
        int mid = (l+r) >> 1;//剩下的很正常
        if(kr <= mid) change(p<<1,l,mid,kl,kr,zl,zr,val);
        else if(kl > mid) change(p<<1|1,mid+1,r,kl,kr,zl,zr,val);
        else change(p<<1,l,mid,kl,mid,zl,zr,val),change(p<<1|1,mid+1,r,mid+1,kr,zl,zr,val);
    }
    int query(int p,int l,int r,int kl,int kr,int zl,int zr)
    {
        if(l == kl && r == kr) return mx[p].query(1,1,m,zl,zr);
        int cur = tag[p].query(1,1,m,zl,zr),mid = (l+r) >> 1;
        if(kr <= mid) cur = max(cur,query(p<<1,l,mid,kl,kr,zl,zr));
        else if(kl > mid) cur = max(cur,query(p<<1|1,mid+1,r,kl,kr,zl,zr));
        else cur = max(cur,max(query(p<<1,l,mid,kl,mid,zl,zr),query(p<<1|1,mid+1,r,mid+1,kr,zl,zr)));
        return cur;//这里每次在修改的时候都要与tag比较,完全覆盖的时候不用,因为已经更新过了
    }
}t;

int main()
{
    n = read(),m = read(),k = read();
    while(k--)
    {
    d = read(),s = read(),h = read(),x = read(),y = read();
    int g = t.query(1,1,n,x+1,x+d,y+1,y+s);//查询最大值
    t.change(1,1,n,x+1,x+d,y+1,y+s,g+h);//区间修改
    }
    printf("%d
",t.query(1,1,n,1,n,1,m));
    return 0;
}
原文地址:https://www.cnblogs.com/captain1/p/9746364.html