一道经典的线段树题目,被我拿来SPLAY练手了,。
熟悉下区间求和,lazy区间更新。
SPLAY的旋转操作真心太强了,从AVL学习了旋转之后迫不及待地学习了SPLAY。
SPLAY原本也是二叉树,我们对SPLAY的层次结构并不关注,只需要注意到任意旋转操作不会改变中序遍历,于是就可以用来维护数列了。
有个技巧就是预先插入两个无关节点,ROOT和ROOT->right_kid。之后把要操作的节点都插入到ROOT->right_kid的左子树,这样就可以确保整个区间都可以操作。
于是对[L,R]区间操作时就可以把L-1对应的节点旋转到根,把R+1对应的节点旋转成根的右儿子,所以对ROOT->right_kid的左子树就是对应整个区间了。
需要注意的是无关节点要真正做到无关,比如我们要维护最小值,那么这两个节点的值要置为INF,否则统计到root可能会出错。
#include<algorithm> #include<cstdio> #define lson(x) (kid[x][0]) #define rson(x) (kid[x][1]) #define ll long long int using namespace std; const int N = 100005; int pre[N], size[N], kid[N][2]; ll data[N], sum[N], lazy[N]; int root, cnt; void NewNode(int &x, int val, int f){ x = ++cnt; pre[x] = f; size[x] = 1; data[x] = sum[x] = val; lazy[x] = lson(x) = rson(x) = 0; } void push_up(int x){ size[x] = size[lson(x)] + size[rson(x)] + 1; sum[x] = sum[lson(x)] + sum[rson(x)] + data[x] + lazy[x]; } void push_down(int x){ ll &k = lazy[x]; if(k){ data[x] += k; lazy[lson(x)] += k; sum[lson(x)] += k*size[lson(x)]; lazy[rson(x)] += k; sum[rson(x)] += k*size[rson(x)]; k = 0; } } void Rotate(int x, int f){ int y = pre[x]; push_down(y); push_down(x); kid[y][!f] = kid[x][f]; pre[kid[x][f]] = y; if(pre[y]) kid[pre[y]][ kid[pre[y]][1] == y] = x; pre[x] = pre[y]; kid[x][f] = y; pre[y] = x; push_up(y); //这里不必更新x } void Splay(int x, int goal){ //用两个单旋代替双旋偷懒orz, 其实还可以更加偷懒 push_down(x); while(pre[x] != goal){ if(pre[pre[x]] == goal){ Rotate(x, kid[pre[x]][0] == x); } else{ int y = pre[x], z = pre[y]; int f = (kid[z][0] == y); if(kid[y][f] == x){ Rotate(x, !f); Rotate(x, f); } else{ Rotate(y, f); Rotate(x, f); } } } push_up(x); if(goal == 0) root = x; } void RotateTo(int k, int goal){ int x = root; push_down(x); while(size[lson(x)] != k){ if(k < size[lson(x)]){ x = lson(x); } else{ k -= (size[lson(x)]+1); x = rson(x); } push_down(x); } Splay(x, goal); } int n, q; int a[N]; void build(int &x, int l, int r, int f){ if(l > r) return; int m = (l+r) >> 1; NewNode(x, a[m], f); build(lson(x), l, m-1, x); build(rson(x), m+1, r, x); push_up(x); } void query(int l, int r){ RotateTo(l-1, 0); RotateTo(r+1, root); printf("%lld\n", sum[lson(rson(root))]); } void update(int l, int r){ ll tmp; scanf("%lld%*c", &tmp); RotateTo(l-1, 0); RotateTo(r+1, root); lazy[lson(rson(root))] += tmp; sum[lson(rson(root))] += tmp*size[lson(rson(root))]; } int main(){ char p; int n1, n2; while(scanf("%d %d%*c", &n, &q) != EOF){ for(int i = 0; i < n; ++i) scanf("%d%*c", &a[i]); root = cnt = 0; pre[0] = kid[0][0] = kid[0][1] = size[0] = data[0] = lazy[0] = 0; sum[0] = 0; NewNode(root, -1, 0); //这里无关节点不会影响数列,因为操作数列时已经将数列旋转到ROOT->right_kid的左子树。 NewNode(rson(root), -1, root); size[root] = 2; build(lson(rson(root)), 0, n-1,rson(root)); push_up(rson(root)); push_up(root); while(q--){ scanf("%c %d %d%*c", &p, &n1, &n2); if(p == 'Q') query(n1, n2); else if(p == 'C') update(n1, n2); } } }