【HDU6873】Game(splay)
题意:
现在有 \(n\) 列,每列有 \(a_i\) 个格子。然后会有两种操作:
- "1 x y",表示在 \((x, y)\) 处向左推动,这里推动能将上方格子一起推动,格子到达第一列就不能再动,如果有格子“悬空”那么就会下落。对于这个操作要输出移动了上方多少格子;
- "2 x",询问第\(x\)列有多少格子。
思路:
显然对于一操作最前面和最后面我们特殊处理,那么中间就相当于区间偏移。
所以我们维护一颗平衡树,我用的splay,那么区间偏移就相当于在树中去掉第 \(l\) 大的结点,然后在第 \(r\) 个位置重新添加一个结点。只要这棵树的结构没有发生变化(即中序遍历出来还是1~n的序列)就行。
具体来说首先将第 \(l-1\) 个结点旋到根结点,然后将第 \(l+1\) 个结点旋为 \(l-1\) 的子节点,那么直接在左儿子删除就行。插入也类似。
修改操作解决了那么就剩下查询,我们要查询 \(1\)~\(r\) 中第一个比 \(y\) 小的位置,这里我们直接对每个结点维护子树最小值即可。那么之后直接在splay上面类似于二分查找就行,找尽可能往右的结点。
注意:当我们添加、删除或者更改了某个结点的信息过后,记得splay来更新其它结点的信息,否则就可能会出错。
细节见代码:
// Author : heyuhhh
// Created Time : 2020/08/18 20:18:00
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 4e5 + 5;
int a[N];
ll all;
int n, q;
struct Splay{
int f[N], ch[N][2], size[N], rev[N] = {0}, val[N], minv[N], root, sz;
ll sum[N];
inline void init() {
root = sz = size[0] = rev[0] = f[0] = 0; val[0] = minv[0] = INF;
}
inline void clear(int x) {
ch[x][0] = ch[x][1] = f[x] = sum[x] = size[x] = 0; val[x] = minv[x] = INF;
}
inline int newnode(int fa, int d, int v) {
++sz;
ch[sz][0] = ch[sz][1] = 0;
f[sz] = fa;
ch[fa][d] = sz;
val[sz] = v;
return sz;
}
inline int get(int x){return ch[f[x]][1] == x;}
inline void update(int x){
size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
minv[x] = min(val[x], min(minv[ch[x][0]], minv[ch[x][1]]));
sum[x] = val[x] + sum[ch[x][0]] + sum[ch[x][1]];
}
// 建二叉搜索树
int build(int l, int r, int rt){
if (l > r) return 0;
int m = (l + r) >> 1, tot = ++sz;
val[tot] = a[m]; f[tot] = rt;
ch[tot][0] = build(l, m - 1, tot);
ch[tot][1] = build(m + 1, r, tot);
update(tot);
return tot;
}
inline void rot(int x){
int old = f[x], oldf = f[old], tp = get(x);
ch[old][tp] = ch[x][tp ^ 1]; f[ch[old][tp]] = old;
ch[x][tp ^ 1] = old; f[old] = x;
f[x] = oldf;
if (oldf) ch[oldf][ch[oldf][1] == old] = x;
update(old); update(x);
}
inline void splay(int x, int tar){ // 旋转到对应位置
for (int fa; (fa = f[x]) != tar; rot(x))
if (f[fa] != tar)
rot(get(fa) == get(x) ? fa : x);
if (!tar) root = x;
}
inline int rk(int k){
int tot = root;
while (1){
if (k <= size[ch[tot][0]]) tot = ch[tot][0];
else {
k -= size[ch[tot][0]] + 1;
if (!k) return tot;
tot = ch[tot][1];
}
}
}
inline int find(int r, int v) {
int x = rk(r);
if (val[x] < v) return r;
splay(x, 0);
int y = ch[x][0];
int res = 0;
while (y) {
if (ch[y][1] && minv[ch[y][1]] < v) res += size[ch[y][0]] + 1, y = ch[y][1];
else if (val[y] < v) return res + size[ch[y][0]] + 1;
else if (ch[y][0] && minv[ch[y][0]] < v) y = ch[y][0];
else return 0;
}
return 0;
}
inline int query(int x) {
x = rk(x);
return val[x];
}
inline ll queryS(int l, int r) {
int x = rk(l - 1);
splay(x, 0);
if (r == n) return sum[ch[x][1]];
int y = rk(r + 1);
splay(y, x);
return sum[ch[y][0]];
}
inline void modify(int p, int v) {
int x = rk(p);
val[x] = v;
update(x);
splay(x, 0);
}
inline void move(int l, int r, int v) {
// 单独提出l这个位置,直接删除
int x = rk(l - 1);
splay(x, 0);
int y = rk(l + 1);
splay(y, x);
clear(ch[y][0]);
ch[y][0] = 0;
update(y);
// 找到应该插的位置,直接插入
int now = rk(r - 1);
splay(now, 0);
if (r == n) {
ch[now][1] = newnode(now, 1, v);
update(ch[now][1]);
splay(sz, 0);
} else {
int tmp = rk(r);
splay(tmp, now);
ch[tmp][0] = newnode(tmp, 0, v);
update(ch[tmp][0]);
splay(sz, 0);
}
}
}tr;
void run() {
all = 0;
FI(n), FI(q);
for (int i = 1; i <= n; i++) {
FI(a[i]);
all += a[i];
}
tr.init();
tr.root = tr.build(1, n, 0);
while (q--) {
int op;
FI(op);
if (op == 1) {
int x, y;
FI(x), FI(y);
int L = tr.find(x, y);
if (L == 0 || L == x) {
FO(0), FO('\n');
continue;
}
++L;
int R = x;
ll res = tr.queryS(L, R) - 1ll * (R - L + 1) * (y - 1);
FO(res), FO('\n');
int h = tr.query(L);
tr.modify(L - 1, tr.query(L - 1) + h - (y - 1));
if (L + 1 >= R) tr.modify(L, tr.query(R)), tr.modify(R, y - 1);
else tr.move(L, R, y - 1);
} else {
int x;
FI(x);
int ans = tr.query(x);
FO(ans), FO('\n');
}
}
for (int i = 1; i <= n; i++) {
FO(tr.query(i));
if (i != n) FO(' ');
else FO('\n');
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
freopen("output.out", "w", stdout);
#endif
int T; FI(T); while(T--)
run();
Flush;
return 0;
}
重要的是自信,一旦有了自信,人就会赢得一切。