【HDU 6888】Art Class 线段树

【HDU 6888】Art Class 线段树

【题意】

​ 在二维平面上,给出 n ( n ≤ 2 × 1 0 5 ) n(n\leq 2\times 10^5) n(n2×105)个矩形,矩形的长和宽均和坐标轴平行,且每个矩形下方的边与 x x x轴所在直线重合。

​ 每个矩形用三个参数表示 1 ≤ l < r ≤ 1 0 9 , 1 ≤ h ≤ 1 0 9 1\leq l<r\leq 10^9,1\leq h\leq 10^9 1l<r1091h109,表示矩形的左下角在 ( l , 0 ) (l,0) (l,0),右上角在 ( r , h ) (r,h) (r,h)

​ 按顺序逐个给出矩形,给出的矩形区域涂黑,每给出一个矩形,问当前涂黑的区域的图形的周长和是多少。

【参考解法】

​ 初步分析,涂黑的区域不一定是凸多边形,因此这道题不简单。

​ 题目中宽度范围很大,但是垂直于 x x x轴的边最多只有 2 n 2n 2n条,因此可以根据这些垂直边的位置作离散化,得到最多 2 n − 1 2n-1 2n1个条形区域。用数组记录每个条形区域在原来的图中对应的宽度。

​ 这些条形区域互不影响,刚开始的值(覆盖高度)都是0.

​ 可以把周长分为两个部分做统计:

​ 1.统计平行于 x x x轴的边的长度。

​ 2.统计平行于 y y y轴的边的长度。

​ 第1部分其实就是有颜色的区间宽度的和的两倍,用线段树很好求,因此主要思考第2部分的长度。

​ 我们发现,每次添加一个高 h h h的矩形,实际上相当于对区间 [ l , r ] [l,r] [l,r]范围内的条形的高度进行了一次区间元素逐个与 h h h max ⁡ \max max

​ 这里就要用到一种线段树的思想,即大部分选手所说的“Segment Tree Breats”或者“吉司机线段树”。追根溯源,可以在《国家集训队2016论文集》第99页找到吉如一前辈的论文:《区间最值操作和历史最值问题》。

​ 而本题,只需要用到其中处理区间最值操作的思想就可以了,也就是,用线段树维护区间的最小值 m i mi mi和严格次小值 s m i smi smi,在区间最值操作的时候,根据 h h h m i mi mi s m i smi smi的大小关系,决定是直接操作打tag,还是继续递归,或是直接返回。

​ 考虑下面一种线段树节点的定义:

struct node{
    
    
        tag//区间求值操作的tag
        mi,smi//区间最小值,严格次小值
        cnt//区间中,最小值的段数。比如111311411中,最小值1就有3段
        
        //上面的变量是处理区间最值操作的经典配置。
  			//下面的变量是完成本题操作求值的关键所在。
          
  			cover//区间覆盖操作的tag
        wid//区间宽度
        len//区间中,被矩形覆盖的部分的宽度之和
        sum//区间中,所有竖边对周长的贡献总和(不考虑区间左右两端的贡献)
        lv,rv//区间左右端点的值
    }

​ 注意一点,相邻两个位置之间高度有差距,就会对答案产生大小为两值之差的竖线贡献。

​ 其中,值sum是求解最后答案的关键量,而sum的维护正是依靠于值cnt来做到的。当区间中 m i < h < s m i mi<h<smi mi<h<smi的时候,所有最小值 m i mi mi将增加到 h h h,这个时候区间中,竖线的贡献会发生变化,会减少。相邻的最小值一起增加,它们之间的竖线贡献是不会变的,只有某个最小值位置旁边存在非最小值时,会减少竖线的贡献。而正好可以通过cnt与区间两端的细节,快速计算出竖线贡献的减少量。

​ 最后求解的答案是root.len*2 + root.lv + root.rv + root.sum;(伪代码)

​ 也就是 水平边 + 两端的竖线 + 总区间中的竖线

​ 在线段树中按照定义维护即可,区间合并操作非常关键,细节比较多。

​ 复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#define George_Plover
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <ctime>
#define MAXN 510001
#define LL long long
#define INF (-1)
using namespace std;
int T,n,m;
struct Command{
    
    
    int l,r,h;
    int l_new,r_new;
}c[MAXN];
pair<int, int> p[MAXN];
int cntp;

int w[MAXN];
struct Segment_Tree{
    
    
    struct node{
    
    
        int l,r;
        int tag,cover;
        int len;
        LL sum;
        int lv,rv;
        //LL milen;
        int mi,smi;
        int cnt;
        int wid;
    }tr[MAXN*4];
    int num;
    
    void init()
    {
    
    
        num=1;
    }
    
    inline LL Abs(LL x)
    {
    
    
        return x<0?-x:x;
    }
    
    inline int Min(int &x,int &y)
    {
    
    
        return x>y?y:x;
    }
    
    inline void Check_max(int &x,int y)
    {
    
    
        if(y>x)x=y;
    }
    
    void pushup(int x)
    {
    
    
        int L=tr[x].l,R=tr[x].r;
        tr[x].len=tr[L].len+tr[R].len;
        tr[x].sum=tr[L].sum+tr[R].sum+Abs(tr[L].rv-tr[R].lv);
        tr[x].lv=tr[L].lv;tr[x].rv=tr[R].rv;
        if(tr[L].mi==tr[R].mi)
        {
    
    
            tr[x].smi=Min(tr[L].smi,tr[R].smi);
            tr[x].mi=tr[L].mi;
            tr[x].cnt=tr[L].cnt+tr[R].cnt+(tr[L].rv==tr[R].lv&&tr[L].rv==tr[x].mi?-1:0);
        }
        else if(tr[L].mi<tr[R].mi)
        {
    
    
            tr[x].mi=tr[L].mi;
            tr[x].smi=Min(tr[R].mi,tr[L].smi);
            tr[x].cnt=tr[L].cnt;
        }
        else
        {
    
    
            tr[x].mi=tr[R].mi;
            tr[x].smi=Min(tr[R].smi,tr[L].mi);
            tr[x].cnt=tr[R].cnt;
        }
    }
    
    void Build(int x,int L,int R)
    {
    
    
        tr[x].tag=INF;tr[x].cover=0;
        tr[x].smi=1+(tr[x].mi=2000000000);
        if(L==R)
        {
    
    
            tr[x].cnt=1;
            tr[x].sum=tr[x].mi=tr[x].lv=tr[x].rv=0;
            tr[x].wid=w[L];/*tr[x].milen=*/tr[x].len=0;
            return;
        }
        int mid=(L+R)/2;
        tr[x].l=++num;Build(num,L,mid);
        tr[x].r=++num;Build(num,mid+1,R);
        pushup(x);
        tr[x].wid=tr[tr[x].l].wid+tr[tr[x].r].wid;
    }
    
    void pushdown(int x)
    {
    
    
        int L=tr[x].l,R=tr[x].r;
        if(tr[x].cover)
        {
    
    
            tr[L].len=tr[L].wid;tr[L].cover=1;
            tr[R].len=tr[R].wid;tr[R].cover=1;
            tr[x].cover=0;
        }
        if(tr[x].tag!=INF)
        {
    
    
            int tmp;
            if(tr[x].tag > tr[L].mi && tr[x].tag < tr[L].smi)
            {
    
    
                Check_max(tr[L].tag,tr[x].tag);
                tmp=0;
                if(tr[L].lv==tr[L].mi)tmp++;
                if(tr[L].rv==tr[L].mi)tmp++;
                Check_max(tr[L].lv,tr[x].tag);
                Check_max(tr[L].rv,tr[x].tag);
                
                tr[L].sum-=(tr[L].cnt*2ll-tmp)*1ll*(tr[x].tag-tr[L].mi);
                tr[L].mi=tr[x].tag;
            }
            if(tr[x].tag > tr[R].mi && tr[x].tag < tr[R].smi)
            {
    
    
                Check_max(tr[R].tag,tr[x].tag);
                tmp=0;
                if(tr[R].lv==tr[R].mi)tmp++;
                if(tr[R].rv==tr[R].mi)tmp++;
                Check_max(tr[R].lv,tr[x].tag);
                Check_max(tr[R].rv,tr[x].tag);
                
                tr[R].sum-=(tr[R].cnt*2ll-tmp)*1ll*(tr[x].tag-tr[R].mi);
                tr[R].mi=tr[x].tag;
            }
            tr[x].tag=INF;
        }
    }
    
    void Modify(int x,int L,int R,int al,int ar,int v)
    {
    
    
        //printf("%d %d %d %d %d %d %d\n",L,R,v,tr[x].mi,tr[x].smi,al,ar);
        if(ar<L||R<al||v<=tr[x].mi)
            return;
        if(al<=L&&R<=ar&&v<tr[x].smi)
        {
    
    
            tr[x].cover=1;tr[x].len=tr[x].wid;
            
            Check_max(tr[x].tag,v);
            int tmp=0;
            if(tr[x].lv==tr[x].mi)tmp++;
            if(tr[x].rv==tr[x].mi)tmp++;
            Check_max(tr[x].lv,v);
            Check_max(tr[x].rv,v);
            tr[x].sum-=(tr[x].cnt*2ll-tmp)*1ll*(v-tr[x].mi);
            tr[x].mi=v;
            return;
        }
        pushdown(x);
        int mid=(L+R)/2;
        Modify(tr[x].l,L,mid,al,ar,v);
        Modify(tr[x].r,mid+1,R,al,ar,v);
        pushup(x);
    }
    
    LL Query()
    {
    
    
        return tr[1].len*2ll+tr[1].lv+tr[1].rv+tr[1].sum;
    }
}seg;
int main()
{
    
    
    scanf("%d",&T);
    while(T--)
    {
    
    
        seg.init();
        cntp=0;
        
        scanf("%d",&n);
        //n=200000;
        for(int i=1;i<=n;i++)
        {
    
    
            scanf("%d%d%d",&c[i].l,&c[i].r,&c[i].h);
            //c[i].l=rand()%1000000000+1;
            //c[i].r=rand()%1000000000+1;
            //if(c[i].r<c[i].l)swap(c[i].r,c[i].l);
            //if(c[i].r==c[i].l)c[i].l==1?c[i].r++:c[i].l--;
            //c[i].h=rand()%1000000000+1;
            
            p[++cntp]=make_pair(c[i].l,i);
            p[++cntp]=make_pair(c[i].r,i);
        }
        sort(p+1,p+cntp+1);
        p[0].first=-1;
        int j=0;
        for(int i=1;i<=cntp;i++)
        {
    
    
            if(p[i].first!=p[i-1].first)
            {
    
    
                w[j]=p[i].first-p[i-1].first;
                j++;
            }
            if(p[i].first==c[p[i].second].l)
                c[p[i].second].l_new = j;
            else
                c[p[i].second].r_new = j;
        }
        j--;
        
        seg.Build(1,1,j);
        
        for(int i=1;i<=n;i++)
        {
    
    
            seg.Modify(1,1,j,c[i].l_new,c[i].r_new-1,c[i].h);
            printf("%lld\n",seg.Query());
        }
    }
    //cout<<"done"<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/George_Plover/article/details/108720572