[NOI2005]维护数列
题解 [NOI2005]维护数列
关于fhq_treap的大小分裂应用非常巧妙的一道题。a了之后可以大大加深对花Qtreap的理解
建树
建树就是一个考点,总不能n次merge吧。利用到了treap的线性构造。欢迎在本博客上阅读《数据结构 线性构造treap》
插入
这个嘛,线性构造一个treap,然后插入即可:
void insert(int k){
root=merge(root,newnode(k));
}
删除
void delate(int pos,int len){
split(root,pos-1,x,y);
split(y,len,y,z);
root=merge(x,z);
recycle(y);
}
修改
分裂之后打标记,在每一次merge与split时下放即可
当初理解这个做法的时候花了一点时间:因为有点怀疑,直接将标记打在节点上,会不会标记下放的时候放到了不该放的地方?
自然不会。如果在split的时候遇到了标记,说明标记是针对全体的,自然下放;如果在merge的时候遇到了标记,说明标记是针对于各自区间的,在merge之前各自下放到自己的区间,就不怕merge之后标记混乱
void turn(int pos,int len,int k){
split(root,pos-1,x,y);
split(y,len,y,z);
lazy_turn[y]=k;
x=merge(x,y); root=merge(x,z);
}
反转
同样利用到标记。有标记的话反转两个儿子即可
void reverse(int pos,int len){
split(root,pos-1,x,y);
split(y,len,y,z);
lazy_rev[y]^=1;
x=merge(x,y); root=merge(x,z);
}
增添上下放标记的特殊分裂和合并
下放标记:
void pushdown(int k){
if(lazy_turn[k]!=INF){
val[k]=lazy_turn[k];
sum[k]=lazy_turn[k]*size[k];
if(lazy_turn[k]>=0) sum_pre[k]=sum_suf[k]=sum_tot[k]=sum[k];
else sum_pre[k]=sum_suf[k]=sum_tot[k]=val[k];
lazy_turn[ch[k][0]]=lazy_turn[k];
lazy_turn[ch[k][1]]=lazy_turn[k];
lazy_turn[k]=INF;
}
if(lazy_rev[k]){
swap(sum_suf[k],sum_pre[k]);
swap(ch[k][0],ch[k][1]);
lazy_rev[ch[k][0]]^=1;
lazy_rev[ch[k][1]]^=1;
lazy_rev[k]=0;
}
}
分裂:
void split(int now,int k,int &x,int &y){
if(!now) x=y=0;
else{
pushdown(now);
int t=size[ch[now][0]]+1;
if(t<=k) {x=now; split(ch[now][1],k-t,ch[now][1],y);}
else {y=now; split(ch[now][0],k,x,ch[now][0]);}
updata(now);
}
}
合并:
int merge(int a,int b){
pushdown(a); pushdown(b);
if(!a||!b){
pushdown(a+b);
updata(a+b);
return a+b;
}
if(rad[a]<rad[b]){
if(lazy_rev[a]||lazy_turn[a]!=INF) pushdown(a);
ch[a][1]=merge(ch[a][1],b); updata(a); return a;
}
else{
if(lazy_rev[b]||lazy_turn[b]!=INF) pushdown(b);
ch[b][0]=merge(a,ch[b][0]); updata(b); return b;
}
}
区间求和
简单维护一下sum即可:
int qsum(int pos,int len){
split(root,pos-1,x,y);
split(y,len,y,z);
int ans=sum[y];
x=merge(x,y); root=merge(x,z);
return ans;
}
最大子段和
这是一个难点。
设sum_pre[k]表示k区间紧挨左面的最大字段和,同理sum_suf[k]为紧靠右面的;sum_tot[k]为区间最大字段和。那么把updata稍作修改
\[sum_{pre}[k]=max(sum_{pre}[lson],sum[lson]+val[k],sum[lson]+val[k]+sum_{pre}[lson]
\]
\[sum_{suf}[k]=max(sum_{suf}[rson],val[k]+sum[rson]+sum_{suf}[lson]+val[k]+sum[rson]
\]
\[sum_{tot}[k]=max(val[k],sum_{tot}[lson],sum_{tot}[rson],sum_{suf}[lson]+val[k],val[k]+sum_{pre}[rson],sum_{suf}[lson]+val[k]+sum_{pre}[rson]
\]
每一次updata的时候更新一边:
void upd(int &n,int k){
n=max(n,k);
}
void updata(int k){
int lson=ch[k][0],rson=ch[k][1];
pushdown(lson);
pushdown(rson);
size[k]=1+size[lson]+size[rson];
sum[k]=val[k]+sum[lson]+sum[rson];
sum_pre[k]=sum_suf[k]=(size[k]==1?val[k]:-INF); sum_tot[k]=val[k];
upd(sum_pre[k],sum_pre[lson]);
upd(sum_pre[k],max(sum[lson]+val[k],sum[lson]+val[k]+sum_pre[rson]));
upd(sum_suf[k],sum_suf[rson]);
upd(sum_suf[k],max(sum[rson]+val[k],sum_suf[lson]+val[k]+sum[rson]));
upd(sum_tot[k],val[k]);
upd(sum_tot[k],max(sum_tot[lson],sum_tot[rson]));
upd(sum_tot[k],max(sum_suf[lson]+val[k],sum_pre[rson]+val[k]));
upd(sum_tot[k],sum_suf[lson]+val[k]+sum_pre[rson]);
}
空间
这道题的空间很吃紧,需要利用到内存池思想
开一个队列记录所有没有使用过的编号。每一次需要增加点的时候从队列中取出来一个;遇到需要回收的子树,就暴力枚举一遍,插到队列中,作为新的可以使用的编号
void recycle(int k){
line.push(k);
if(ch[k][0]) recycle(ch[k][0]);
if(ch[k][1]) recycle(ch[k][1]);
}
而当你把上述的一切的一切全部考虑充分,你就成功切了一道NOI难度的数据结构题φ(゜▽゜*)♪
完整代码:
// luogu-judger-enable-o2
#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define FT fhq_treap::
inline int read();
namespace fhq_treap{
const int MAX=5e5+5,INF=0x3f3f3f3f;
int tot,root,x,y,z;
int ch[MAX][2],val[MAX],rad[MAX],size[MAX];
int sum[MAX],sum_tot[MAX],sum_pre[MAX],sum_suf[MAX];
int lazy_rev[MAX],lazy_turn[MAX];
queue <int> line;
void init(){
for(register int i=1;i<=500000;++i) line.push(i);
}
void upd(int &n,int k){
n=max(n,k);
}
int newnode(int k){
tot=line.front(); line.pop();
val[tot]=sum[tot]=sum_suf[tot]=sum_pre[tot]=sum_tot[tot]=k;
size[tot]=1;
rad[tot]=rand();
lazy_turn[tot]=INF; lazy_rev[tot]=0;
ch[tot][0]=ch[tot][1]=0;
return tot;
}
void recycle(int k){
line.push(k);
if(ch[k][0]) recycle(ch[k][0]);
if(ch[k][1]) recycle(ch[k][1]);
}
void pushdown(int k){
if(lazy_turn[k]!=INF){
val[k]=lazy_turn[k];
sum[k]=lazy_turn[k]*size[k];
if(lazy_turn[k]>=0) sum_pre[k]=sum_suf[k]=sum_tot[k]=sum[k];
else sum_pre[k]=sum_suf[k]=sum_tot[k]=val[k];
lazy_turn[ch[k][0]]=lazy_turn[k];
lazy_turn[ch[k][1]]=lazy_turn[k];
lazy_turn[k]=INF;
}
if(lazy_rev[k]){
swap(sum_suf[k],sum_pre[k]);
swap(ch[k][0],ch[k][1]);
lazy_rev[ch[k][0]]^=1;
lazy_rev[ch[k][1]]^=1;
lazy_rev[k]=0;
}
}
void updata(int k){
int lson=ch[k][0],rson=ch[k][1];
pushdown(lson);
pushdown(rson);
size[k]=1+size[lson]+size[rson];
sum[k]=val[k]+sum[lson]+sum[rson];
sum_pre[k]=sum_suf[k]=(size[k]==1?val[k]:-INF); sum_tot[k]=val[k];
upd(sum_pre[k],sum_pre[lson]);
upd(sum_pre[k],max(sum[lson]+val[k],sum[lson]+val[k]+sum_pre[rson]));
upd(sum_suf[k],sum_suf[rson]);
upd(sum_suf[k],max(sum[rson]+val[k],sum_suf[lson]+val[k]+sum[rson]));
upd(sum_tot[k],val[k]);
upd(sum_tot[k],max(sum_tot[lson],sum_tot[rson]));
upd(sum_tot[k],max(sum_suf[lson]+val[k],sum_pre[rson]+val[k]));
upd(sum_tot[k],sum_suf[lson]+val[k]+sum_pre[rson]);
}
int build(int len){
stack <int> tmp;
for(int i=1;i<=len;++i){
int j=0,now=newnode(read());
while(!tmp.empty()&&rad[now]<rad[tmp.top()]){
ch[tmp.top()][1]=j;
j=tmp.top();
updata(j);
ch[now][0]=tmp.top();
tmp.pop();
}
updata(now);
if(!tmp.empty()) ch[tmp.top()][1]=now,updata(tmp.top());
tmp.push(now);
}
int u;
while(!tmp.empty()) u=tmp.top(),updata(u),tmp.pop();
return u;
}
void split(int now,int k,int &x,int &y){
if(!now) x=y=0;
else{
pushdown(now);
int t=size[ch[now][0]]+1;
if(t<=k) {x=now; split(ch[now][1],k-t,ch[now][1],y);}
else {y=now; split(ch[now][0],k,x,ch[now][0]);}
updata(now);
}
}
int merge(int a,int b){
pushdown(a); pushdown(b);
if(!a||!b){
pushdown(a+b);
updata(a+b);
return a+b;
}
if(rad[a]<rad[b]){
if(lazy_rev[a]||lazy_turn[a]!=INF) pushdown(a);
ch[a][1]=merge(ch[a][1],b); updata(a); return a;
}
else{
if(lazy_rev[b]||lazy_turn[b]!=INF) pushdown(b);
ch[b][0]=merge(a,ch[b][0]); updata(b); return b;
}
}
void original(int len){
root=build(len);
}
void insert(int k){
root=merge(root,newnode(k));
}
void delate(int pos,int len){
split(root,pos-1,x,y);
split(y,len,y,z);
root=merge(x,z);
recycle(y);
}
int qsum(int pos,int len){
split(root,pos-1,x,y);
split(y,len,y,z);
int ans=sum[y];
x=merge(x,y); root=merge(x,z);
return ans;
}
void reverse(int pos,int len){
split(root,pos-1,x,y);
split(y,len,y,z);
lazy_rev[y]^=1;
x=merge(x,y); root=merge(x,z);
}
void turn(int pos,int len,int k){
split(root,pos-1,x,y);
split(y,len,y,z);
lazy_turn[y]=k;
x=merge(x,y); root=merge(x,z);
}
int maxline(){
return sum_tot[root];
}
void getin(int pos,int tot){
split(root,pos,x,y);
// for(int i=1;i<=tot;++i) x=merge(x,newnode(read()));
x=merge(x,build(tot));
root=merge(x,y);
}
void debug(int k){
cout<<"This is No."<<k<< " checking.."<<endl;
for(int i=1;i<=size[root];++i) printf("%d ",qsum(i,1)); printf("\n");
// for(int i=1;i<=size[root];++i) qsum(i,1);
printf("\n");
}
}
int n,m;
int main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
#endif
n=read(); m=read(); FT init();
FT original(n);
for(register int i=1;i<=m;++i){
char type[10]; int a,b,c; cin>>type;
switch(type[0]){
case 'I':
a=read(); b=read();
FT getin(a,b);
break;
case 'D':
a=read(); b=read();
FT delate(a,b);
break;
case 'M':
if(type[2]=='K'){
a=read(); b=read(); c=read();
FT turn(a,b,c);
}
else{
printf("%d\n",FT maxline());
}
break;
case 'R':
a=read(); b=read();
FT reverse(a,b);
break;
case 'G':
a=read(); b=read();
printf("%d\n",FT qsum(a,b));
break;
}
// FT debug(i);
}
return 0;
}
inline int read(){
char tmp=getchar(); int sum=0; bool flag=false;
while(tmp<'0'||tmp>'9'){
if(tmp=='-') flag=true;
tmp=getchar();
}
while(tmp>='0'&&tmp<='9'){
sum=(sum<<1)+(sum<<3)+tmp-'0';
tmp=getchar();
}
return flag?-sum:sum;
}