Splay(伸展树)/HDU6873


题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=6873

题目大意

给定一组 \(n\) 列的方块,每列方块数 \(b_i\) ,现有 \(q\) 次操作/询问。
\(O = 1\) ,则将第 \(x\) 列第 \(y\) 个及其以上的方块向左推1格, \(x\) 列左侧部分方块也随之运动(如果存在,且能推动),并回答推动的方块总数目。
\(O = 2\) ,则回答第 \(x\) 列当前的方块数目。
注意:方块会受重力下落。

题目解析



使用 \(Splay\) 维护方块序列,时间复杂度:建树 \(O(n \log n)\) 、查询、删除和插入均为 \(O(\log n)\) ,因此总复杂度为 \(O((n+q) \log n)\) ,可以通过该题。
具体参见代码。

参考代码

#include 
#define N 400005
#define EMPTY 0
#define INF INT_MAX
#define ll long long
using namespace std;
struct Tree{
    int data, dataMin, dataMax, size, fa, child[2];
    ll dataSum;
} t[N]; //其中data, fa, child为节点的基本属性
int cnt, root;
vector  dataset, nodeBin;

inline void read(int &s) { //快读,支持int
    s = 0;
    int tt = 1, k = getchar();
    for (; k < '0' || k > '9'; k = getchar()) if (k == '-') tt = -1;//判断该数正负
    for (; k >= '0' && k <= '9'; k = getchar()) s = s * 10+(k ^ 48);//^48相当于-‘0’,较快。
    s *= tt;
}
inline void write(ll s) { //快写,支持int和long long
    int tt = 0, a[40];
    if (s < 0) putchar('-'), s = -s;
    do { a[++tt] = s % 10; } while (s /= 10);//用do while就不用特判一个0
    while(tt) putchar(48+a[tt--]);
}
inline int checkSize(int x) { return (x == EMPTY) ? 0 : t[x].size;}
inline int checkDataMin(int x) { return (x == EMPTY) ? INF : t[x].dataMin;}
inline int checkDataMax(int x) { return (x == EMPTY) ? -INF : t[x].dataMax;}
inline ll checkDataSum(int x) { return (x == EMPTY) ? 0 : t[x].dataSum;}
inline void updNode(int x) {
    t[x].size = checkSize(t[x].child[0]) + checkSize(t[x].child[1]) + 1;
    t[x].dataMin = min(t[x].data, min(checkDataMin(t[x].child[0]), checkDataMin(t[x].child[1])));
    t[x].dataMax = max(t[x].data, max(checkDataMax(t[x].child[0]), checkDataMax(t[x].child[1])));
    t[x].dataSum = checkDataSum(t[x].child[0]) + checkDataSum(t[x].child[1]) + t[x].data;
}
void rotate(int x, int o)
{
    int y = t[x].fa;
    if (!y) return;
    int z = t[y].fa;
    t[y].child[o^1] = t[x].child[o];
    if (t[x].child[o] != EMPTY)	t[t[x].child[o]].fa = y;
    t[x].fa = z;
    if (z != EMPTY)
    {
        if (t[z].child[0] == y) t[z].child[0] = x;
        else t[z].child[1] = x;
    }
    t[x].child[o] = y;
    t[y].fa = x;
    updNode(y);
    updNode(x);
}
void splay(int x)
{
    if (x == EMPTY) return;
    int y;
    while (t[x].fa != EMPTY)
    {
        y = t[x].fa;
        if (t[y].fa == EMPTY) //旋转后为根节点
        {
            if (t[y].child[0] == x) rotate(x, 1);
            else rotate(x, 0);
            break;
        }
        else {
            if (t[t[y].fa].child[1] == y)
            {
                if (t[y].child[0] == x) rotate(x, 1), rotate(x, 0);
                else rotate(y, 0), rotate(x, 0);
            }
            else {
                if (t[y].child[1] == x) rotate(x, 0), rotate(x, 1);
                else rotate(y, 1), rotate(x, 1);
            }
        }
    }
    root = x;
}

inline int mininum(int x) { //找x的子树中序号最小的
    while (t[x].child[0] != EMPTY) x = t[x].child[0];
    return x;
}
inline int maxinum(int x) { //找x的子树中序号最大的
    while (t[x].child[1] != EMPTY) x = t[x].child[1];
    return x;
}
inline int succ(int x) { //找x的后继
    splay(x);
    if (t[x].child[1] == EMPTY) return EMPTY;
    return mininum(t[x].child[1]);
}
inline int prec(int x) { //找x的前驱
    splay(x);
    if (t[x].child[0] == EMPTY) return EMPTY;
    return maxinum(t[x].child[0]);
}
int createNode(int data) //新建节点,存放data(优先取用废弃内存池)
{
    if (nodeBin.empty())
    {
        t[++cnt] = (Tree){data, data, data, 1, EMPTY, EMPTY, EMPTY, data};
        return cnt;
    }
    int x = nodeBin.back();
    t[x] = (Tree){data, data, data, 1, EMPTY, EMPTY, EMPTY, data};
    nodeBin.pop_back();
    return x;
}
int findKth(int x, int k) //找序号为k的节点
{
    while (true)
    {
        if (x == EMPTY) return EMPTY;
        int lc = checkSize(t[x].child[0]);
        if (k <= lc) x = t[x].child[0];
        else {
            if (k == lc+1) return x;
            else {x = t[x].child[1], k -= lc+1;}
        }
    }
}
inline int getKth(int x) { //找节点x的序号k
    splay(x);
    return checkSize(t[x].child[0]) + 1;
}
void insertKth(int x, int k) //将单节点x插入树中的序号k的位置
{
    if (!root) {root = x; return;}
    if (k <= 0 || k > t[root].size+1) return;
    if (k == 1) {
        int y = mininum(root);
        if (y == EMPTY) return;
        splay(y);
        t[y].child[0] = x;
        t[x].fa = y;
        updNode(y);
        return;
    }
    int y = findKth(root, k-1);
    if (y == EMPTY) return;
    splay(y);
    t[x].child[1] = t[y].child[1];
    if (t[y].child[1] != EMPTY) t[t[y].child[1]].fa = x;
    t[y].child[1] = EMPTY;
    t[x].child[0] = y;
    t[y].fa= x;
    root = x;
    updNode(y);
    updNode(x);
}
void deleteKth(int k) //删除树上序号为k的节点
{
    if (!root) {return;}
    if (k <= 0 || k > t[root].size) return;
    if (k == 1) {
        int y = mininum(root);
        if (y == EMPTY) return;
        nodeBin.push_back(y);
        splay(y);
        if (t[y].child[1] != EMPTY) t[t[y].child[1]].fa = EMPTY, root = t[y].child[1];
        else root = 0;
        return;
    }
    int y = findKth(root, k);
    if (y == EMPTY) return;
    splay(y);
    nodeBin.push_back(y);
    int z = prec(y);
    t[z].child[1] = t[y].child[1];
    if (t[y].child[1] != EMPTY) t[t[y].child[1]].fa = z;
    t[t[y].child[0]].fa = EMPTY;
    root = t[y].child[0];
    while (z != EMPTY)
    {
        updNode(z);
        z = t[z].fa;
    }
}
int buildTree(int L, int R, int fa) //建树,data取用dataset[L]~[R],L>0
{
    if (L > R) return EMPTY;
    if (L == R) {
       t[L] = (Tree){dataset[L], dataset[L], dataset[L], 1, fa, EMPTY, EMPTY, dataset[L]};
       return L;
   }
   int mid = (L+R)/2;
   t[mid] = (Tree){dataset[mid], 0, 0, 0, fa, EMPTY, EMPTY, 0};
   t[mid].child[0] = buildTree(L, mid-1, mid);
   t[mid].child[1] = buildTree(mid+1, R, mid);
   updNode(mid);
   return mid;
}
inline void clearAll() { //清空全部
    cnt = 0;
    dataset.clear();
    nodeBin.clear();
    root = 0;
}
int findData(int x, int data) //找到x的子树上<=Data的最大序号的节点
{
    int a = EMPTY;
    if (x == EMPTY) return EMPTY;
    if (data < t[x].dataMin) return EMPTY;
    if (t[x].child[1] != EMPTY) {
        if (data >= checkDataMin(t[x].child[1])) {
            a = findData(t[x].child[1], data);
        }
    }
    if (a != EMPTY) return a;
    if (t[x].data <= data) return x;
    if (t[x].child[0] != EMPTY) {
        if (data >= checkDataMin(t[x].child[0])) {
            a = findData(t[x].child[0], data);
        }
    }
    if (a != EMPTY) return a;
    return EMPTY;
}
inline void printTree() { //将树上每个节点data按序输出
    int x = mininum(root);
    while (x != EMPTY) {
        write(t[x].data);
        x = succ(x);
        if (x != EMPTY) putchar(' ');
    }
    putchar('\n');
}
int main()
{
    int T, n, q;
    read(T);
    while (T--) {
        clearAll();
        read(n), read(q);
        dataset.push_back(0);
        for (int i = 0; i < n; ++i) {
            int x;
            read(x);
            dataset.push_back(x);
        }
        root = buildTree(1, n, EMPTY);
        while (q--)
        {
            int o, x, y, now;
            read(o);
            if (o == 1)
            {
                read(x), read(y);
                now = findKth(root, x);
                if (t[now].data < y) {putchar(48), putchar('\n'); continue;}
                splay(now);
                int p = findData(t[now].child[0], y-1);
                if (p == EMPTY) {putchar(48), putchar('\n'); continue;}
                ll tot = checkDataSum(t[now].child[0]) + t[now].data;
                int r = succ(p);
                if (r != EMPTY)
                {
                    splay(r);
                    tot -= checkDataSum(t[r].child[0]);
                    t[r].data -= y-1 - t[p].data;
                }
                tot -= (ll)(getKth(now) - getKth(p))*(y-1);
                deleteKth(getKth(p));
                insertKth(createNode(y-1), x);
                write(tot);
                putchar('\n');
            }
            else {
                read(x);
                now = findKth(root, x);
                if (now != EMPTY) write(t[now].data);
                else putchar(48);
                putchar('\n');
            }
        }
        printTree();
    }
    return 0;
}

感谢支持!