[BZOJ3845] ZCC loves Minecraft

不得不说出题人的创意特赞
题面翻译(BZOJ的英文题能不能带翻译QaQ我一开始没看见多组数据):
在一个平面直角坐标系中,有一些关键点,坐标都是整数。现在要求你构造一个点集:
1.包含了所有关键点
2.允许由无数个点叠成一条线段,或是平面
3.任意一条平行于x轴或y轴的直线,与这个点集的公共部分,必须是:空,一个点,或连续的一条线段(也就是中间不能断)
4.满足1,3的条件下,使点集中的平面的面积最小

现在一开始平面上没有关键点,N次操作,每次加入一个关键点,输出N行,每行代表此时的点集的最小面积

题目保证,在任意时刻,整个点集一定是联通的,也就是保证不存在一个不联通的点集满足1和3

设所有x的最小值是lmost,最大值是rmost。根据3性质,对于一条直线X=x0,它与点集的公共部分是线段

(x0,DOWNx0)(x0,UPx0)(lmost<=x<=rmost)

以UP[lmost..rmost]为例,因为3性质,不存在一个i使得

UPi1>UPi<UPi+1(lmost<i<rmost)

也就是说,UP一定是一个先不下降,后不上升的序列。同理,DOWN先不上升后不下降

当你新加入一个关键点导致UP改变时,有如下两种情况//设原先的y最大值是T
图1是y<=T 图2是Y>T 的情况。
Fig.1:y不超过原最大值;Fig.2:y超过原最大值
于是我们可以考虑对于所有整数lmost<=X<=rmost,用线段树维护一个UP(DOWN)序列,支持以下操作:
1.区间(单点)变成同一个数
2.询问一个位置X,求X左侧/右侧最近的且>=UP[X]的UP[ANS]

然而怎么得到输出的答案?

先令所有DOWN=0,UP都>0

ANS=i=lmostrmost1min(UP[i],UP[i+1])

上式的正确性是显而易见的,又因为UP是先不降后不升序列,设UP[mid]是最大值,那么:
min(UP[i],UP[i+1])=UP[i](i<mid)

min(UP[i],UP[i+1])=UP[i+1](i>=mid)

结合上式,
ANS=(i=lmostrmostUP[i])maxi=rmosti=lmostUP[i]

那么如果有DOWN存在,结论也是很容易的:
ANS=(i=lmostrmostUP[i])(i=lmostrmostDOWN[i])maxrmosti=lmostUP[i]+minrmosti=lmostDOWN[i]

你可以把DOWN记成相反数,那么只要让你的线段树兹磁区间和询问和区间最大值询问就可以啦!
代码长度不忍直视>_<

#include <cstdio>
template <typename Int> inline Int max(Int x, Int y) { return x > y ? x : y; }
template <typename Int> inline Int min(Int x, Int y) { return x < y ? x : y; }
int I()
{
    char c = getchar();
    int r = 0, f = 0;
    while ((c < 48 or c > 57) && c != '-')
        c = getchar();
    if (c == '-')
        f = 1, c = getchar();
    while (c > 47 && c < 58)
        r = (r << 3) + r + r + c - 48, c = getchar();
    return f ? -r : r;
}
class segmentTree
{
    public:
    void PUT(int x, int y)
    {
        if (QMax(x, x) < y)
        {
            if (y <= SegMax[1])
            {
                int LMt = x > -100000 ? (Ql = -100000, Qr = x - 1, Qw = y, RMostLarger(1, -100000, 100000)) : -1234321237, RMt = x < 100000 ? (Ql = x + 1, Qr = 100000, Qw = y, LMostLarger(1, -100000, 100000)) : 1234321237;
                if (LMt != -1234321237)
                    Same(LMt + 1, x, y);
                else
                    Same(x, RMt - 1, y);
            }
            else
            {
                int TOPp = MaxPosition(), X = leaf_ref[TOPp], Y = SegMax[TOPp];
                if (X < x)
                    Same(X, x - 1, Y);
                if (X > x)
                    Same(x + 1, X, Y);
                Set(x, y);
            }
        }
    }
    void init() { __init(1, -100000, 100000); }
    void Set(int x, int d) { M(x, d); }
    void Same(int l, int r, int d) { Ql = l, Qr = r, Qw = d, P(1, -100000, 100000); }
    int QMax(int l, int r) { Ql = l, Qr = r; return G(1, -100000, 100000); }
    long long QSum(int l, int r) { Ql = l, Qr = r; return S(1, -100000, 100000); }
    int MaxPosition()
    {
        int p = 1;
        while (Size[p] > 1)
        {
            Down(p);
            if (SegMax[p] == SegMax[p + p])
                p <<= 1;
            else
                p = p + p + 1;
        }
        return p;
    }
    private:
    int RMostLarger(int p, int l, int r)
    {
        if (SegMax[p] < Qw)
            return -1234321237;
        if (Ql <= l && r <= Qr)
        {
            int m;
            while (Size[p] > 1)
            {
                m = l + r >> 1;
                Down(p);
                if (SegMax[p + p + 1] >= Qw)
                    l = m + 1, p = p + p + 1;
                else
                    r = m, p <<= 1;
            }
            return leaf_ref[p];
        }
        int __, m = l + r >> 1;
        if (Qr > m)
            if ((__ = RMostLarger(p + p + 1, m + 1, r)) != -1234321237)
                return __;
        return Ql <= m ? RMostLarger(p + p, l, m) : -1234321237;
    }
    int LMostLarger(int p, int l, int r)
    {
        if (SegMax[p] < Qw)
            return 1234321237;
        if (Ql <= l && r <= Qr)
        {
            int m;
            while (Size[p] > 1)
            {
                m = l + r >> 1;
                Down(p);
                if (SegMax[p + p] >= Qw)
                    r = m, p <<= 1;
                else
                    l = m + 1, p = p + p + 1;
            }
            return leaf_ref[p];
        }
        int __, m = l + r >> 1;
        if (Ql <= m)
            if ((__ = LMostLarger(p + p, l, m)) != 1234321237)
                return __;
        return Qr > m ? LMostLarger(p + p + 1, m + 1, r) : 1234321237;
    }
    int Ql, Qr, Qw;
    int SegMax[555555], SameTag[555555], Size[555555], leaf_ref[555555];
    long long SegSum[555555];
    void __init(int p, int l, int r)
    {
        Size[p] = r - l + 1;
        if (l < r)
        {
            int m = l + r >> 1;
            __init(p + p, l, m);
            __init(p + p + 1, m + 1, r);
        }
        else
            leaf_ref[p] = l;
    }
    void Up(int p)
    {
        SegMax[p] = max(SegMax[p + p], SegMax[p + p + 1]);
        SegSum[p] = SegSum[p + p] + SegSum[p + p + 1];
    }
    void Down(int p)
    {
        if (SameTag[p])
        {
            SameTag[p] = 0;
            SameTag[p + p] = SameTag[p + p + 1] = 1;
            SegMax[p + p] = SegMax[p + p + 1] = SegMax[p];
            SegSum[p + p] = (long long)Size[p + p] * SegMax[p];
            SegSum[p + p + 1] = (long long)Size[p + p + 1] * SegMax[p];
        }
    }
    int Find(int x)
    {
        int p = 1, l = -100000, r = 100000, m;
        Down(p);
        while (l < r)
        {
            Down(p);
            m = l + r >> 1;
            if (x <= m)
                r = m, p <<= 1;
            else
                l = m + 1, p = p + p + 1;
        }
        return p;
    }
    void P(int p, int l, int r) // makeSame
    {
        if (Ql <= l && r <= Qr)
            SameTag[p] = 1, SegMax[p] = Qw, SegSum[p] = (long long)Size[p] * Qw;
        else
        {
            int m = l + r >> 1;
            Down(p);
            if (Ql <= m)
                P(p + p, l, m);
            if (Qr > m)
                P(p + p + 1, m + 1, r);
            Up(p);
        }
    }
    int G(int p, int l, int r) // get the largest
    {
        if (Ql <= l && r <= Qr)
            return SegMax[p];
        Down(p);
        int m = l + r >> 1;
        int Le = Ql <= m ? G(p + p, l, m) : 0, Ri = Qr > m ? G(p + p + 1, m + 1, r) : 0;
        return max(Le, Ri);
    }
    void M(int x, int d) // a[x] := d
    {
        int p = Find(x);
        SegMax[p] = SegSum[p] = d, SameTag[p] = 0;
        while (p > 1)
            p >>= 1, Up(p);
    }
    long long S(int p, int l, int r) // get the sum
    {
        if (Ql <= l && r <= Qr)
            return SegSum[p];
        Down(p);
        int m = l + r >> 1;
        long long Le = Ql <= m ? S(p + p, l, m) : 0, Ri = Qr > m ? S(p + p + 1, m + 1, r) : 0;
        return Le + Ri;
    }
}UP, DOWN;
int main()
{
    UP.init(), DOWN.init();
    int Q;
    while (scanf("%d", &Q) != EOF)
    {
        int lmost = 10000000, rmost = -10000000;
        UP.Same(-100000, 100000, 0), DOWN.Same(-100000, 100000, 0);
        while (Q--)
        {
            int x = I(), y = I();
            UP.PUT(x, 200000 + y), DOWN.PUT(x, 200000 - y);
            if (x < lmost)
                lmost = x;
            if (x > rmost)
                rmost = x;
            printf("%lld\n", UP.QSum(-100000, 100000) + DOWN.QSum(-100000, 100000) - UP.QMax(-100000, 100000) - DOWN.QMax(-100000, 100000) - 400000ll * (rmost - lmost));
        }
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值