题目链接
可以说这道题很好的给我们讲述了在Splay树上的lazy标记的递推,跟线段树上类似,在这棵二叉搜索树上,我们一样的去递推懒标记,接下来说说在哪几处需要专门注意懒标记的使用。
这里有几处需要注意的地方,就是一开始给你的元素不是已经排好序的,不是固定顺序的(这题似乎本身就是诡异,什么都不讲明白,连数据范围都没说明白)但是仍然是一道好题的说,至少是第一次这么充分的接触到了lazy标记。
首先就是第一项的操作,给予(l, r)区间都加上值T,这就是将(l-1)这个点提出来,再提出(r+1)然后链接起来,在后者的左子树上推add标记即可。
第二项的操作是反转操作,跟上一个相似的,我们依旧这么处理,在根据二叉树的性质,只需要从一个头节点往下去不断的交换左右的位置关系,就可以达到翻转的作用了。
第三项操作是把(l, r)后面T个元素不断的放到头上去的这样一个操作,简单的可以看成是两个区间相互交换位置,(l, r-T)与(r-T+1, r)来交换位置,所以,不妨将后面的那个取出来,然后再插到前面那个前面即可。
第四项操作是在第X节点后面插入P权值节点,直接用上之前的思维即可。
第五项操作是删除操作,删除第K个节点,也同上的想法。
第六项的操作就是求【l, r】的区间最小值,那么就是将这段区间提出来,然后输出其最上面的头的求得的最小值即可,当然,这样子要求的基础就是lazy标记的下移还有回来的更新操作相互作用,才能达到这样的直接输出。
好了,上面的操作我们讲完了,接下来讲一下懒标记。
我们需要在哪些地方向下推懒标记呢?在进行旋转(不是题目中的个操作旋转,是Splay本身所具备的旋转操作)的时候,我们会发现会改变原有的splay树的形状,所以,在将x节点和y节点以及z节点改变位置的时候,我们首先要做的就是pusndown()的操作,然后当每次的x与y换好位置的时候,需要逐步更新y和x,就是需要向上pushup()了;在Splay()的时候树的形状也会改变,所以,我们在将x挪到goal的下面的时候,需要同时去pushdown()祖父z、父亲y、自己本身x这三个节点,然后就可以了;再之后,可以看到,求区间第K个的时候,需要向下去寻找,此时,因为有翻转操作,所以我们应该先pushdown()再去查询,所以在这里也要向下推标记。
还有一个特别的地方就是一开始按序输入的N个数的地方,需要有一些小的处理,可以变得方便的多,我们可以每次插入一个数,并且将其Splay()到根节点上去,然后下一次的插入就直接接在根节点的右子树上就可以了。
还剩下的就是一些基础了,具体看一下代码了。(对了,最后有一组测试样例)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#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 long long ll;
const int maxN = 1e5 + 7;
int N, M, root, tot;
struct node
{
int ff, val, ch[2], size, cnt, lazy, add, minn;
node() { ff = val = ch[0] = ch[1] = size = cnt = lazy = add = 0; minn = INF; }
void init(int fa, int _val) { ff = fa; val = minn = _val; ch[0] = ch[1] = lazy = add = 0; size = cnt = 1; }
}t[maxN<<1];
void pushup(int rt)
{
if(!rt) return;
t[rt].size = t[t[rt].ch[0]].size + t[t[rt].ch[1]].size + t[rt]t;
t[rt].minn = t[rt].val;
if(t[rt].ch[0]) t[rt].minn = min(t[rt].minn, t[t[rt].ch[0]].minn);
if(t[rt].ch[1]) t[rt].minn = min(t[rt].minn, t[t[rt].ch[1]].minn);
}
void pushdown(int rt)
{
if(!rt) return;
if(t[rt].add)
{
if(t[rt].ch[0])
{
t[t[rt].ch[0]].add += t[rt].add;
t[t[rt].ch[0]].val += t[rt].add;
t[t[rt].ch[0]].minn += t[rt].add;
}
if(t[rt].ch[1])
{
t[t[rt].ch[1]].add += t[rt].add;
t[t[rt].ch[1]].val += t[rt].add;
t[t[rt].ch[1]].minn += t[rt].add;
}
t[rt].add = 0;
}
if(t[rt].lazy)
{
t[rt].lazy = 0;
if(t[rt].ch[0]) t[t[rt].ch[0]].lazy ^= 1;
if(t[rt].ch[1]) t[t[rt].ch[1]].lazy ^= 1;
swap(t[rt].ch[0], t[rt].ch[1]);
}
}
void Rotate(int x)
{
int y = t[x].ff, z = t[y].ff;
pushdown(y); pushdown(x);
int 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);
}
void Splay(int x, int goal)
{
pushdown(x);
while(t[x].ff != goal)
{
int y = t[x].ff, z = t[y].ff;
pushdown(z); pushdown(y); pushdown(x); //这里就要下传标记了,避免树形改变导致的旋转出错
if(z != goal) (t[z].ch[0] == y) ^ (t[y].ch[0] == x) ? Rotate(x) : Rotate(y);
Rotate(x);
}
if(!goal) root = x;
}
void insert(int x)
{
int u = ++tot;
t[root].ch[1] = u;
t[u].init(root, x);
Splay(u, 0);
if(!root) root = u;
}
int Kth(int x)
{
int u = root;
while(true)
{
pushdown(u);
int y = t[u].ch[0];
if(t[y].size + t[u]t < x)
{
x -= t[y].size + t[u]t;
u = t[u].ch[1];
}
else
{
if(t[y].size >= x) u = y;
else return u;
}
}
}
void ADD(int l, int r, int val)
{
int x = Kth(l - 1), y = Kth(r + 1);
Splay(x, 0); Splay(y, x);
int tmp = t[y].ch[0];
if(tmp)
{
t[tmp].val += val;
t[tmp].add += val;
t[tmp].minn += val;
}
}
void REVOLVE(int l1, int r1, int l2, int r2) //将(l1, r1)与区间(l2, r2)交换位置
{
int x = Kth(l2 - 1), y = Kth(r2 + 1);
Splay(x, 0); Splay(y, x);
int tmp = t[y].ch[0];
t[y].ch[0] = 0;
x = Kth(l1 - 1); y = Kth(l1);
Splay(x, 0); Splay(y, x);
t[y].ch[0] = tmp;
t[tmp].ff = y;
}
inline void init()
{
root = tot = 0;
memset(t, 0, sizeof(t));
insert(INF);
}
char op[10];
int main()
{
while(scanf("%d", &N) != EOF)
{
init();
for(int i=1, val; i<=N; i++)
{
scanf("%d", &val);
insert(val);
}
insert(INF);
scanf("%d", &M);
int x, y, T;
while(M--)
{
scanf("%s", op);
scanf("%d", &x);
if(op[0] == 'A') //ADD
{
scanf("%d", &y);
scanf("%d", &T);
ADD(x+1, y+1, T);
}
else if(op[0] == 'I') //INSERT:第x数后面插入值为y的数
{
scanf("%d", &y);
int l = Kth(x + 1), r = Kth(x + 2);
Splay(l, 0); Splay(r, l);
int tmp = ++tot;
t[r].ch[0] = tmp;
t[tmp].val = y;
t[tmp].size = 1;
t[tmp]t = 1;
t[tmp].ff = r;
t[tmp].lazy = t[tmp].add = 0;
t[tmp].ch[0] = t[tmp].ch[1] = 0;
Splay(tmp, 0);
}
else if(op[0] == 'D') //DELETE
{
int l = Kth(x), r = Kth(x + 2);
Splay(l, 0); Splay(r, l);
t[r].ch[0] = 0;
pushup(t[root].ch[1]);
pushup(root);
}
else if(op[0] == 'M') //MIN
{
scanf("%d", &y);
int l = Kth(x), r = Kth(y + 2);
Splay(l, 0); Splay(r, l);
int tmp = t[r].ch[0];
printf("%d\n", t[tmp].minn);
}
else if(op[3] == 'E') //REVERSE
{
scanf("%d", &y);
int l = Kth(x), r = Kth(y + 2);
Splay(l, 0); Splay(r, l);
int tmp = t[r].ch[0];
t[tmp].lazy ^= 1;
}
else //REVOLVE
{
scanf("%d%d", &y, &T);
T = T%(y - x + 1);
if(T) REVOLVE(x + 1, y - T + 1, y - T + 2, y + 1);
}
}
}
return 0;
}
/*
10
1 2 3 4 5 6 7 8 9 10
15
ADD 4 8 3
MIN 5 7
MIN 7 10
REVERSE 2 5
MIN 2 6
MIN 2 3
INSERT 3 4
MIN 3 4
MIN 5 10
DELETE 6
MIN 3 5
MIN 4 4
REVOLVE 3 6 7
MIN 5 8
MIN 7 10
ans:
8
9
2
7
4
2
3
4
7
9
*/
更多推荐
SuperMemo 【POJ - 3580】【Splay+懒标记递推想法】
发布评论