Game【HDU-6873】【Splay】

2020 Multi-University Training Contest 9 G题


  题意:有N个有各自高度的位置,按1~N从左到右排列。现在我们有两种操作:

  1. x y将第x列,第y行的方块,包括它上面的方块从右往左的移动过去,同时推动前面的方块前进,当然,最前面的那个方块,会根据重力掉下去。非法状态(或者不能移动状态)不移动。
  2. x,查询第x列的方块高度是多少。

  于是,很明显的,我们可以考虑大于等于y的最长向前的连续,我们找到第一个小于y的高度的列的位置beg_{pos},那么,从beg_{pos} + 1到x的这个连续段内肯定是都要往前推的,不考虑特殊情况,那么可以看成beg_{pos} + 2到x向前移动一格,然后原本beg_{pos} + 1的这个点的高度就被删去了,再后来,第x位的值会被改成y - 1,并且原本beg_{pos}的高度会加上beg_{pos} + 1溢出的块的个数。

  于是,我们可以用一个平衡树数据结构来维护一下这个。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e5 + 7;
int N, Q, b[maxN];
namespace Splay_Tree
{
#define Min_3(a, b, c) min(a, min(b, c))
    int root;
    struct node
    {
        int ff, h, siz, ch[2], mn_h;
        ll sum_h;
        inline void Init(int height, int fa)
        {
            ff = fa; h = sum_h = height;
            ch[0] = ch[1] = 0;
            siz = 1;
        }
    } t[maxN];
    inline void pushup(int u)
    {
        t[u].siz = t[t[u].ch[0]].siz + t[t[u].ch[1]].siz + 1;
        t[u].sum_h = t[t[u].ch[0]].sum_h + t[t[u].ch[1]].sum_h + t[u].h;
        t[u].mn_h = Min_3(t[t[u].ch[0]].mn_h, t[t[u].ch[1]].mn_h, t[u].h);
    }
    int build(int fa, int l, int r)
    {
        if(l > r) return 0;
        int mid = HalF;
        t[mid].Init(b[mid], fa);
        t[mid].ch[0] = build(mid, l, mid - 1);
        t[mid].ch[1] = build(mid, mid + 1, r);
        pushup(mid);
        return mid;
    }
    inline void Rotate(int x)
    {
        int y = t[x].ff, z = t[y].ff, k = t[y].ch[1] == x;
        t[z].ch[t[z].ch[1] == y] = x;
        t[x].ff = z;
        t[y].ch[k] = t[x].ch[k ^ 1];
        t[t[x].ch[k ^ 1]].ff = y;
        t[x].ch[k ^ 1] = y;
        t[y].ff = x;
        pushup(y);
        pushup(x);
    }
    inline void Splay(int x, int goal)
    {
        while(t[x].ff ^ goal)
        {
            int y = t[x].ff, z = t[y].ff;
            if(z ^ goal) (t[z].ch[0] == y) ^ (t[y].ch[0] == x) ? Rotate(x) : Rotate(y);
            Rotate(x);
        }
        if(!goal) root = x;
    }
    inline int Kth(int x)
    {
        int u = root, y;
        while(true)
        {
            y = t[u].ch[0];
            if(x > t[y].siz + 1)
            {
                x -= t[y].siz + 1;
                u = t[u].ch[1];
            }
            else
            {
                if(t[y].siz >= x) u = y;
                else return u;
            }
        }
    }
    inline int find_pre(int u, int y)
    {
        u = t[u].ch[0];
        while (true)
        {
            if(t[t[u].ch[1]].mn_h < y) u = t[u].ch[1];
            else if(t[u].h < y) return u;
            else u = t[u].ch[0];
        }
    }
};
using namespace Splay_Tree;
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &N, &Q);
        for(int i=1; i<=N; i++)
        {
            scanf("%d", &b[i]);
        }
        t[0].mn_h = INF; t[0].siz = 0; t[0].h = 0; t[0].sum_h = 0;
        Splay_Tree::root = build(0, 1, N);
        ll ans, tmp;
        for(int i=1, op, x, y, pos, beg_pos, u, det, fir; i<=Q; i++)
        {
            scanf("%d%d", &op, &x);
            if(op == 1)
            {
                scanf("%d", &y);
                pos = Kth(x);
                Splay(pos, 0);
                if(y > t[pos].h || t[t[pos].ch[0]].mn_h >= y)
                {
                    printf("0\n");
                    continue;
                }
                beg_pos = find_pre(pos, y);
                Splay(beg_pos, pos);
                tmp = t[t[beg_pos].ch[1]].siz;
                ans = t[t[beg_pos].ch[1]].sum_h - tmp * (y - 1) + t[pos].h - y + 1;
                printf("%lld\n", ans);
                if(t[beg_pos].ch[1])
                {
                    u = t[beg_pos].ch[1];
                    while(t[u].ch[0]) u = t[u].ch[0];
                    fir = u;
                    det = t[u].h - y + 1;
                    t[beg_pos].h += det;
                    /*Solve*/
                    Splay(fir, beg_pos);
                    t[fir].h = y - 1;
                    t[beg_pos].ch[1] = t[fir].ch[1];
                    t[t[fir].ch[1]].ff = beg_pos;
                    t[fir].ch[1] = t[pos].ch[1];
                    t[t[pos].ch[1]].ff = fir;
                    t[pos].ch[1] = fir;
                    t[fir].ff = pos;
                    pushup(fir);
                    pushup(beg_pos);
                    pushup(pos);
                    /*end*/
                }
                else
                {
                    det = t[pos].h - y + 1;
                    t[beg_pos].h += det;
                    t[pos].h = y - 1;
                    pushup(beg_pos);
                    pushup(pos);
                }
            }
            else
            {
                pos = Kth(x);
                printf("%d\n", t[pos].h);
            }
        }
        for(int i=1, pos; i<=N; i++)
        {
            pos = Kth(i);
            printf("%d%c", t[pos].h, i == N ? '\n' : ' ');
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值