题意就是一个简单的平衡树的板子,但是在实现上有很多细节,蒟蒻的我竟然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;
}
再加一个洛谷上相似的例题:
SP4487 GSS6 - Can you answer these queries VI
只是把区间操作换成了单点
code
#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]].maxsum;
}
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;
char s[2];
scanf("%d",&n);
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);
scanf("%d",&q);
while(q--){
scanf("%s",s);
int pos,l,r;
if(s[0] == 'I'){
scanf("%d",&pos);
add(pos,1);
}
if(s[0] == 'D'){
scanf("%d",&pos);
cut(pos + 1,1);
}
if(s[0] == 'R'){
int val;
scanf("%d%d",&pos,&val);
Mak(pos + 1,1,val);
}
if(s[0] == 'Q'){
int l,r;
scanf("%d%d",&l,&r);
int ans = Get_Sum(l + 1,r - l + 1);
printf("%d\n",ans);
}
}
//ss;
return 0;
}