学习链接:https://blog.csdn.net/ztzshiwo_cnyali/article/details/52221503
题目链接:http://poj.org/problem?id=3580
POJ 3580
splay超全模板题...
总共有6种操作,分别为MIN,INSERT,ADD,DELETE,REVERSE,REVOLVE六种,下面会分别介绍格式与方法
首先输入N以及N个数,接下来输入M,即操作个数,接下来M行包含操作,具体格式如下
1.MIN a b 求a,b区间的最小值
2.ADD a b c 对a,b区间的每个数添加c
3.DELETE a 将第a个数删除
4.INSERT a b 在第a个数后面添加一个数b
5.REVERSE a,b 将a,b区间每个数翻转 如 REVERSE 2 4 (1,2,3,4,5)-->(1,4,3,2,5)
6.REVOLVE a,b,c 将a,b区间作为一个环 然后旋转c次(这是我的理解... ...)
如 REVOVLE 2 4 1 (1,2,3,4,5)-->(1,4,2,3,5)
REVOVLE 2 4 2 (1,2,3,4,5)-->(1,3,4,2,5)
然后这里面的c有可能是负数并且很大... ... 所以要取模
讲一下算法思想:
构建一棵splay树 储存父亲 左右儿子 翻转标记 增加标记 最小值 本身的值 以此节点为根的子树的节点数 (size)
首先将1~n节点添加进去 同时要在最前面和最后面添加一个节点(具体理由后面讲)
基本函数:update(更新这个节点的MIN值与size),splay(将一个点旋转到目标位置),rotateto(找到第k个节点并旋转到目标位置),pushdown(将增加标记(add)与翻转标记(rev)推到下一层节点),newnode(构建一个新节点,节点序号为cnt)
讲讲每个操作的具体操作方法
1.MIN a,b 将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,注意:因为前面添加了一个节点,所以原来第a个节点为现在的第a+1个节点 所以现在根节点右节点的左子树就代表以前的 a,b区间,只要输出它的MIN就可以了
2.ADD a,b,c 同理 将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,给根节点右节点的左子树增加标记与数值,最小值添加c
3.DELETE a 将第a个节点旋到根节点,第a+2个节点旋到根节点的右节点,那么根节点右节点的左子树有且仅有a+1这个节点(即原来的第a个节点),那么断绝他们的父子关系就可以了
4.INSERT a b 将第a个节点旋到根节点,第a+1个节点旋到根节点的右节点,那么根节点右节点的左子树绝对没有节点,同样添加一个值为b的新节点,然后建立关系
5.REVERSE a,b 与ADD相似 只要改变一下将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,给根节点右节点的左子树更改REV标记
6.REVOLVE a,b,c这个操作有一点复杂... ...实际上可以看出 这就是将这个区间里面分成两个部分然后交换位置,那我们先通过旋转找到这一段区间,将它从父亲节点那里并记录位置,然后又找到另一端区间的位置,将之前存下来区间放在这个区间的前面,就可以实现,这一段需要一定的理解,可以手动模拟一下发现规律,具体见代码
#include#include #include #include #include const int maxn=400010; using namespace std; int rt,m,n,a[maxn],cnt; int val[maxn],Min[maxn],add[maxn],rev[maxn]; int ls[maxn],rs[maxn],fa[maxn],sz[maxn]; void newnode(int v) { sz[++cnt]=1; val[cnt]=Min[cnt]=v; fa[cnt]=ls[cnt]=rs[cnt]=add[cnt]=rev[cnt]=0; } void update(int x) { sz[x]=sz[ls[x]]+sz[rs[x]]+1; Min[x]=val[x]; if(ls[x]&&Min[ls[x]] Min[ls[x]]; if(rs[x]&&Min[rs[x]] Min[rs[x]]; } void pushdown(int x) { if(add[x]!=0) { if(ls[x]) { add[ls[x]]+=add[x]; Min[ls[x]]+=add[x]; val[ls[x]]+=add[x]; } if(rs[x]) { add[rs[x]]+=add[x]; Min[rs[x]]+=add[x]; val[rs[x]]+=add[x]; } add[x]=0; } if(rev[x]) { if(ls[x])rev[ls[x]]^=1; if(rs[x])rev[rs[x]]^=1; swap(ls[x],rs[x]); rev[x]=0; } } void build(int x,int f) { if(x==n+2)return; newnode(a[x]); if(f) { fa[cnt]=f; rs[f]=cnt; } else rt=cnt; int now=cnt; build(x+1,cnt); update(now); } void init() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(0,0); } void rrotate(int x) { int y=ls[x],k=fa[x]; ls[x]=rs[y]; if(ls[x])fa[ls[x]]=x; fa[x]=y;fa[y]=k;rs[y]=x; if(k==0)rt=y; else if(ls[k]==x)ls[k]=y; else rs[k]=y; update(x); } void lrotate(int x) { int y=rs[x],k=fa[x]; rs[x]=ls[y]; if(rs[x])fa[rs[x]]=x; ls[y]=x;fa[x]=y;fa[y]=k; if(k==0)rt=y; else if(ls[k]==x)ls[k]=y; else rs[k]=y; update(x); } void splay(int x,int f) { int y,z; while(1) { if((y=fa[x])==f)break; if((z=fa[y])==f) { if(ls[y]==x)rrotate(y); else lrotate(y); } else { if(ls[z]==y) { if(ls[y]==x)rrotate(z),rrotate(y); else lrotate(y),rrotate(z); } else { if(rs[y]==x)lrotate(z),lrotate(y); else rrotate(y),lrotate(z); } } } return; } void rotateto(int k,int f) { int i=rt; while(1) { pushdown(i); int lsum=sz[ls[i]]+1; if(k==lsum)break; if(k ls[i]; else k-=lsum,i=rs[i]; } splay(i,f); } void ADD(int x,int y,int z) { rotateto(x,0); rotateto(y+2,rt); int k=ls[rs[rt]]; add[k]+=z,val[k]+=z,Min[k]+=z; } void insert(int x,int y) { rotateto(x+1,0);rotateto(x+2,rt); newnode(y); ls[rs[rt]]=cnt; fa[cnt]=rs[rt]; } void del(int x) { rotateto(x,0);rotateto(x+2,rt); ls[rs[rt]]=0; } void MIN(int x,int y) { rotateto(x,0);rotateto(y+2,rt); printf("%d\n",Min[ls[rs[rt]]]); } void reverse(int x,int y) { if(x==y)return; rotateto(x,0);rotateto(y+2,rt); rev[ls[rs[rt]]]^=1; } void revolve(int x,int y,int z) { int p=(y-x+1),k,t; k=(z%p+p)%p; if(k) { rotateto(y-k+1,0);rotateto(y+2,rt); t=ls[rs[rt]]; ls[rs[rt]]=0; rotateto(x,0);rotateto(x+1,rt); ls[rs[rt]]=t; fa[t]=rs[rt]; } } void solve() { int x,y,z; char s[10]; scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",s); if(s[0]=='A') { scanf("%d%d%d",&x,&y,&z); ADD(x,y,z); } else if(s[0]=='I') { scanf("%d%d",&x,&y); insert(x,y); } else if(s[0]=='D') { scanf("%d",&x); del(x); } else if(s[0]=='M') { scanf("%d%d",&x,&y); MIN(x,y); } else if(s[0]=='R') { if(s[3]=='O') { scanf("%d%d%d",&x,&y,&z); revolve(x,y,z); } else { scanf("%d%d",&x,&y); reverse(x,y); } } } return; } int main() { init(); solve(); return 0; }