题意就是一个简单的平衡树的板子,但是在实现上有很多细节,蒟蒻的我竟然debug了3天,只能说自己的板子不硬,更新下板子
因为细节挺多的(至少我觉得是,所以就直接以注释的形式写在代码里了)但是要注意,这里说的子列是连续的子列。
因为看网上的题解说的好像在bzoj上一直开新点会内存超限,为了防止也出现这个情况,我特意学了一手节点回收分配(虽然最后也没啥用好像)
超长代码(捂脸)
#include <bits/stdc++.h> #define ss system("pause"); using namespace std; const int maxn = 500000; const int INF = 1e9; /******************** splay树 ********************/ struct node { int f,val,size,ch[2],cnt; int sum,lmax,rmax,maxsum; bool rev,tag; node() {} node(int v){ f = ch[0] = ch[1] = rev = tag = 0; size = cnt = 1; val = sum = lmax = rmax = maxsum = v; } }tree[maxn]; int root,sz,top; int org[maxn],sta[maxn]; inline void splay(int x,int goal); int findkth(int x); //节点分配回收机制 inline int new_node() { return top?sta[top--]:++sz; } inline void update(int x) { //对0节点进行初始赋值的原因就在于防止0节点影响对于有负值节点的更新 //会造成负数值的更新出现问题 int lc = tree[x].ch[0],rc = tree[x].ch[1]; tree[x].size = tree[lc].size + tree[rc].size + 1; tree[x].sum = tree[lc].sum + tree[rc].sum + tree[x].val; tree[x].maxsum = max(max(tree[lc].maxsum,tree[rc].maxsum),max(0,tree[lc].rmax) + tree[x].val + max(0,tree[rc].lmax)); tree[x].lmax = max(tree[lc].lmax,max(0,tree[rc].lmax) + tree[x].val + tree[lc].sum); tree[x].rmax = max(tree[rc].rmax,max(0,tree[lc].rmax) + tree[x].val + tree[rc].sum); } void Replace(int x,int d) { if(!x) return ; tree[x].val = d; tree[x].sum = d * tree[x].size; tree[x].maxsum = tree[x].lmax = tree[x].rmax = max(d,d * tree[x].size); tree[x].rev = 0,tree[x].tag = 1; } void Mak(int pos,int tot,int d)//把指定区间全部转化成指定值 { int l = pos - 1,r = pos + tot; l = findkth(l),r = findkth(r); splay(l,0),splay(r,l); int u = tree[root].ch[1]; Replace(tree[u].ch[0],d); update(u),update(root); } inline void rev(int x) { if(tree[x].tag) return ; tree[x].rev ^= 1; swap(tree[x].ch[0],tree[x].ch[1]); swap(tree[x].lmax,tree[x].rmax);//左右的最大值也会跟着反转 } inline void pushdown(int x) { if(tree[x].rev){ if(tree[x].ch[0]) rev(tree[x].ch[0]); if(tree[x].ch[1]) rev(tree[x].ch[1]); tree[x].rev = 0; } if(tree[x].tag){ if(tree[x].ch[0]) Replace(tree[x].ch[0],tree[x].val); if(tree[x].ch[1]) Replace(tree[x].ch[1],tree[x].val); tree[x].tag = 0; } } inline void rotate(int x) { int fa = tree[x].f,gfa = tree[fa].f; pushdown(x),pushdown(fa), pushdown(gfa); //判断x是父节点的哪个儿子,0为左,1为右 int k = (tree[fa].ch[1] == x); //把x的父节点在gfa的位置用x取代 tree[gfa].ch[tree[gfa].ch[1] == fa] = x; tree[x].f = gfa;//x的父亲节点变成了gfa tree[fa].ch[k] = tree[x].ch[k^1]; tree[tree[x].ch[k^1]].f = fa; tree[x].ch[k^1] = fa; tree[fa].f = x; update(fa),update(x); } inline void splay(int x,int goal) { //pushdown(x); while(tree[x].f != goal){ int fa = tree[x].f , gfa = tree[fa].f; if(gfa != goal){ ((tree[gfa].ch[0] == fa) ^ (tree[fa].ch[0] == x)) ? rotate(x) : rotate(fa); } rotate(x); } if(goal == 0) root = x; } void build_tree(int &rt,int l, int r, int fa) { int mid = (l + r) >> 1; rt = new_node(); tree[rt] = node(org[mid]); tree[rt].f = fa; if(l == r) return ; if(l < mid) build_tree(tree[rt].ch[0],l, mid - 1,rt); if(r > mid) build_tree(tree[rt].ch[1],mid + 1,r, rt); update(rt); } int findkth(int x) { if(tree[root].size < x) return -1; int u = root; while(1){ pushdown(u); if(x <= tree[tree[u].ch[0]].size){ u = tree[u].ch[0]; } else if(x <= tree[tree[u].ch[0]].size + tree[u].cnt){ return u; } else{ x -= (tree[tree[u].ch[0]].size + tree[u].cnt); u = tree[u].ch[1]; } } } void Reverse(int x,int tot) { int l = x - 1,r = x + tot; l = findkth(l) , r = findkth(r); splay(l,0),splay(r,l); int u = tree[root].ch[1]; int pos = tree[u].ch[0]; if(tree[pos].tag) return ;//如果这个区间已经被标记整体修改了,就没有必要反转了 rev(pos); //这里反转不能像之前一样打标记就可以了 //因为反转完这个序列的最大左右值肯定会变化 //所以要直接反转完并进行值的更新,这个标记是指的子节点 update(u),update(root); } void erase(int x) { if(!x) return ; sta[++top] = x; erase(tree[x].ch[0]),erase(tree[x].ch[1]); } void cut(int a,int tot) { int l = a - 1,r = a + tot; l = findkth(l) , r = findkth(r); splay(l,0),splay(r,l); int u = tree[root].ch[1]; erase(tree[u].ch[0]),tree[u].ch[0] = 0; update(u),update(root); } void add(int pos,int tot) { for(int i = 1; i <= tot; i++){ scanf("%d",org + i); } //将区间[a,b]后接到c后面 int l = pos, r = pos + 1; l = findkth(l) , r = findkth(r); splay(l,0) , splay(r, l); int u = tree[root].ch[1]; build_tree(tree[u].ch[0],1,tot,u); update(u),update(root); } //获取区间和 int Get_Sum(int pos,int tot) { int l = pos - 1,r = pos + tot; l = findkth(l) , r = findkth(r); splay(l,0), splay(r,l); int u = tree[root].ch[1]; return tree[tree[u].ch[0]].sum; } void inorder(int rt) { if(!rt) return ; pushdown(rt); if(tree[rt].ch[0]) inorder(tree[rt].ch[0]); cout<<tree[rt].val<<' '; if(tree[rt].ch[1]) inorder(tree[rt].ch[1]); } int main() { int n,q; string s; scanf("%d%d",&n,&q); for(int i = 1; i <= n; i++) scanf("%d",org + i + 1); org[1] = org[n + 2] = -INF; //由于不存在的节点标志为0,所以为了防止update时把0节点也错误计算进去,要对0节点进行赋值 //-INF是为了防止更新区间最大值的时候,把一些节点的负数值判定为0,造成部分的最大值的和会偏大 tree[0] = node(-INF),tree[0].size = tree[0].sum = 0; build_tree(root,1,n + 2,0); while(q--){ cin>>s; if(s == "MAX-SUM") printf("%d\n",tree[root].maxsum); else { int pos,tot; scanf("%d%d",&pos,&tot); if(s == "GET-SUM"){ int ans = Get_Sum(pos + 1,tot); printf("%d\n",ans); } if(s == "INSERT") add(pos + 1,tot); if(s == "MAKE-SAME"){ int d; scanf("%d",&d); Mak(pos + 1,tot,d); } if(s == "DELETE") cut(pos + 1,tot); if(s == "REVERSE") Reverse(pos + 1,tot); } //inorder(root); //cout<<endl; } //ss; return 0; }