注:由於本代碼注釋默認各位都已理解了fhqTreap的基本操作,因此本代碼對於例如split,merge等基本
函數的注釋未曾給出。若您看不懂本代碼,您可以先移步這里。
//Treap fhq版(不旋轉) //此模板為平衡樹維護區間操作的模板 //注:在區間操作中split()標准變為子樹大小 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #define INF 0x3f3f3f3f #define MAXN 500001 using namespace std; int n,m,a[MAXN],cnt,size,root; int son[MAXN][2],siz[MAXN],val[MAXN],sum[MAXN],rd[MAXN]; int lazy_revise[MAXN],lazy_reverse[MAXN];//記錄當前節點是否需要修改或翻轉 int tmx[MAXN],lmx[MAXN],rmx[MAXN];//求最大子段和專用數組 //當前節點最大子段和,左兒子最大子段和,右兒子最大子段和 queue<int> trashcan;//回收節點重復利用 inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void update(int x) { if(son[x][0]&&son[x][1]) { siz[x]=siz[son[x][0]]+siz[son[x][1]]+1; sum[x]=sum[son[x][0]]+sum[son[x][1]]+val[x]; tmx[x]=max(tmx[son[x][0]],tmx[son[x][1]]); tmx[x]=max(tmx[x],rmx[son[x][0]]+val[x]+lmx[son[x][1]]); lmx[x]=max(lmx[son[x][0]],sum[son[x][0]]+val[x]+lmx[son[x][1]]); rmx[x]=max(rmx[son[x][1]],sum[son[x][1]]+val[x]+rmx[son[x][0]]); } if(son[x][0]&&!son[x][1]) { siz[x]=siz[son[x][0]]+1; sum[x]=sum[son[x][0]]+val[x]; tmx[x]=max(tmx[son[x][0]],rmx[son[x][0]]+val[x]); lmx[x]=max(lmx[son[x][0]],sum[son[x][0]]+val[x]); lmx[x]=max(0,lmx[x]); rmx[x]=max(0,val[x]+rmx[son[x][0]]); } if(!son[x][0]&&son[x][1]) { siz[x]=siz[son[x][1]]+1; sum[x]=sum[son[x][1]]+val[x]; tmx[x]=max(tmx[son[x][1]],lmx[son[x][1]]+val[x]); rmx[x]=max(rmx[son[x][1]],sum[son[x][1]]+val[x]); rmx[x]=max(0,rmx[x]); lmx[x]=max(0,lmx[son[x][1]]+val[x]); } if(!son[x][0]&&!son[x][1]) { siz[x]=1,sum[x]=tmx[x]=val[x]; lmx[x]=rmx[x]=max(val[x],0); } } int new_node(int k) { int x=0; if(!trashcan.empty()) { x=trashcan.front(); trashcan.pop(); } else x=++cnt; son[x][0]=son[x][1]=0; lazy_reverse[x]=0; lazy_revise[x]=INF; rd[x]=rand(); siz[x]=1; val[x]=sum[x]=tmx[x]=k; lmx[x]=rmx[x]=max(k,0); return x; } int build(int *data,int k)//類似於笛卡爾樹的建樹方式 //笛卡爾樹的建樹方式如下(笛卡爾樹val值滿足小根堆 隨機數值滿足二叉查找樹) //前提新節點插入順序必須val值從小到大插入 以保證新入節點val值必比原有節點大 //棧內存極右鏈 即從棧底到棧頂存儲根節點 根節點的右兒子 根節點的右兒子的右兒子 顯然隨機數值從棧頂到棧底遞減 //進入一個新節點后 從棧頂開始搜索 找到棧中第一個比新入節點隨機數值小的數 //由於隨機數值小 因此新入節點是找到的節點的兒子 又由於val值大 因此新入節點是找到的節點的右兒子 //又因為新入節點的val值大於找到的節點的右兒子的val值 因此原右兒子成為新入節點的左兒子 //由於原右兒子即為找到的節點在棧內上方的元素 因此把此元素修改至左兒子后 把此元素及其上方元素出棧即可 //由於棧中存極右鏈 因此找到的節點將在棧中位於找到的節點的上方 即替代原右兒子的位置 //直至所有點均插入即可 { int now=0,pre=0;//當前節點,找到的節點的原右兒子 static int sta[MAXN],top=0; //static:靜態變量 其在定義時系統將自動將其初始化為0 另外在調用完后不會直接銷毀 而會在下次重新定義同名靜態變量時重新將以前定義過的變量調用 同時保留上次調用完后變量內存放的數據 for(int i=1;i<=k;i++) { now=new_node(data[i]); pre=0; while(top&&rd[sta[top]]>rd[now])//如果棧內有元素且未找到比當前節點隨機數值小的數 { update(sta[top]);//更新出棧節點 pre=sta[top];//記錄右兒子 sta[top--]=0;//先出棧再top-- } if(top)//如果棧內還有元素且找到比當前節點隨機數值小的數 son[sta[top]][1]=now;//新入節點成為找到的節點的右兒子 son[now][0]=pre;//原右兒子成為新入節點的左兒子 sta[++top]=now;//新入節點入棧 } while(top)//棧內還有元素 update(sta[top--]);//全部更新 return sta[1];//棧底元素即為根節點 } void trash(int x) { if(!x) return; trashcan.push(x); trash(son[x][0]); trash(son[x][1]); } void change(int x,int k) { val[x]=k; sum[x]=siz[x]*k; lmx[x]=rmx[x]=max(sum[x],0);//若區間內節點權值全為負數那么不如不取 tmx[x]=max(sum[x],val[x]);//對於當前節點sum()多了一種選擇即節點自己的權值 lazy_revise[x]=k;//標記修改值便於以后pushdown()操作 } void flip(int x) { swap(son[x][0],son[x][1]); swap(lmx[x],rmx[x]); lazy_reverse[x]^=1;//若之前就需要翻轉 那么再翻轉即恢復原狀態 } void pushdown(int x)//區間操作特殊函數 用於處理兩個lazy() { if(lazy_revise[x]!=INF) { if(son[x][0]) change(son[x][0],lazy_revise[x]); if(son[x][1]) change(son[x][1],lazy_revise[x]); } if(lazy_reverse[x]) { if(son[x][0]) flip(son[x][0]); if(son[x][1]) flip(son[x][1]); } lazy_revise[x]=INF; lazy_reverse[x]=0; } void split(int now,int k,int &x,int &y) { if(!now) { x=y=0; return; } pushdown(now); if(siz[son[now][0]]>=k)//待操作區間全部在左子樹 y=now,split(son[now][0],k,x,son[now][0]); else x=now,split(son[now][1],k-siz[son[now][0]]-1,son[now][1],y); update(now); } int merge(int x,int y) { if(x) pushdown(x); if(y) pushdown(y); if(!x||!y) return x+y; if(rd[x]<rd[y]) { son[x][1]=merge(son[x][1],y); update(x); return x; } else { son[y][0]=merge(x,son[y][0]); update(y); return y; } } void insert()//插入 { int pos=read(),len=read(),x,y; int datas[MAXN]; for(int i=1;i<=len;i++) datas[i]=read(); int rt=build(datas,len);//把新節點先處理成一棵Treap split(root,pos,x,y);//沿插入位置分成兩棵Treap root=merge(merge(x,rt),y);//把新Treap直接接在原Treap上 } void del()//刪除 { int pos=read(),len=read(),x1,y1,x2,y2; split(root,pos-1,x1,y1);//把所有要修改的節點都分給y1的Treap split(y1,len,x2,y2);//把所有要刪除的節點都分給x2的Treap 此時x2的Treap上只有需刪除的節點 root=merge(x1,y2);//把剩余的Treap合並 trash(x2);//回收節點,節省空間 } void revise()//修改 { int pos=read(),len=read(),k=read(),x1,y1,x2,y2; split(root,pos-1,x1,y1);//把所有要修改的節點都分給y1的Treap split(y1,len,x2,y2);//把所有要修改的節點都分給x2的Treap 此時x2的Treap上只有需修改的節點 change(x2,k);//只需修改x2的Treap的根節點即x2 因為合並時pushdown()會完成剩余修改 root=merge(x1,merge(x2,y2));//把修改過的Treap同原Treap合並 } void reverse()//翻轉 { int pos=read(),len=read(),x1,y1,x2,y2; split(root,pos-1,x1,y1);//把所有要翻轉的節點都分給y1的Treap split(y1,len,x2,y2);//把所有要翻轉的節點都分給x2的Treap 此時x2的Treap上只有需翻轉的節點 flip(x2);//只需將x2的Treap的根節點即x2的兩個兒子翻轉 因為合並時pushdown()會完成剩余翻轉 root=merge(x1,merge(x2,y2));//把翻轉過的Treap同原Treap合並 } void get_sum()//區間權值和 { int pos=read(),len=read(),x1,y1,x2,y2; split(root,pos-1,x1,y1);//把所求區間分給y1的Treap split(y1,len,x2,y2);//把所求區間分給x2的Treap 此時x2的Treap即為所求區間 printf("%d\n",sum[x2]); root=merge(x1,merge(x2,y2));//把所求區間同原Treap合並 } void sub_sum()//求整個序列內的最大子段和 { printf("%d\n",tmx[root]); } int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); root=build(a,n); for(int i=1;i<=m;i++) { char s[10]; scanf("%s",s); if(s[0]=='I') insert(); if(s[0]=='D') del(); if(s[0]=='M'&&s[2]=='K') revise(); if(s[0]=='R') reverse(); if(s[0]=='G') get_sum(); if(s[0]=='M'&&s[2]=='X') sub_sum(); } return 0; }