Splay(伸展树)/HDU6873

1|0题目链接

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

2|0题目大意

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

3|0题目解析



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

4|0参考代码

#include <bits/stdc++.h> #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 <int> 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; }

感谢支持!


__EOF__

本文作者炯炯目光
本文链接https://www.cnblogs.com/jjmg/p/13595762.html
关于博主:KTH 信息与网络工程硕士在读
版权声明:欢迎分享或转载
声援博主:To be or not to be, is a question.
posted @   Chiron-zy  阅读(273)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示