平衡二叉树 treap
treap通过左右旋维护了一个二叉查找树,根据随机的优先级建立满足优先级大根堆的二叉查找树,在实践中有不错的食府,code也简单。
cogs1829 普通平衡树
题目大意:进行插入、删除、名次、前驱后继。
思路:前面的三种操作都很普通,前驱后继有两种做法(非常不正统吧。。。):1)找到这个数的名次,然后+-1,(如果是后继,并且和原数相同就名次+1,直到不同);2)进行递归查找,以前驱为例:如果这个点的值比x小,就更新一下前驱,找他的右子树,如果>=x,就找他的左子树。这里有可能出现插入多个重复的数,我们可以再开一个域num,记录这种数的个数,然后在操作中相应的进行更改。
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; struct Node{ Node *ch[2]; int r,v,s,num; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();s=1;num=1;} int cmp(int x) const{ if (x==v) return -1; else return x<v?0:1; } void Maintain(){ s=num; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; } }; Node* root; void rotate(Node* &o,int d) { Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o; o->Maintain();k->Maintain();o=k; } void insert(Node* &o,int x) { if (o==NULL) o=new Node(x); else { int d=o->cmp(x); if (d==-1) ++o->num; else { insert(o->ch[d],x); if (o->ch[d] ->r > o->r) rotate(o,d^1); } } o->Maintain(); } void del(Node* &o,int x) { int d=o->cmp(x); if (d==-1) { if (o->num==1) { if (o->ch[0]==NULL) o=o->ch[1]; else { if (o->ch[1]==NULL) o=o->ch[0]; else { int d2=(o->ch[0] ->r > o->ch[1] ->r ? 1 : 0); rotate(o,d2);del(o->ch[d2],x); } } } else --o->num; } else del(o->ch[d],x); if (o!=NULL) o->Maintain(); } int rank(Node* &o,int x) { int d=o->cmp(x); if (d==-1) return (o->ch[0]==NULL ? 1 : o->ch[0] ->s +1); else { if (d==0) return rank(o->ch[d],x); else return rank(o->ch[d],x) + (o->ch[0]==NULL ? o->num : o->ch[0] ->s +o->num); } } int kth(Node* &o,int x) { if (o==NULL||x<=0||x > o->s) return 0; int s=(o->ch[0]==NULL ? 0 : o->ch[0] ->s); if (x>=s+1&&x<=s+ o->num) return o->v; if (x<=s) return kth(o->ch[0],x); else return kth(o->ch[1],x-s- o->num); } int main() { freopen("phs.in","r",stdin); freopen("phs.out","w",stdout); int n,i,j,x,t,k; scanf("%d",&n); for (i=1;i<=n;++i) { scanf("%d%d",&j,&x); if (j==1) insert(root,x); if (j==2) del(root,x); if (j==3) { t=rank(root,x); printf("%d\n",t); } if (j==4) { t=kth(root,x); printf("%d\n",t); } if (j==5) { insert(root,x); k=rank(root,x); t=kth(root,k-1); del(root,x); printf("%d\n",t); } if (j==6) { insert(root,x); k=rank(root,x); t=kth(root,k+1); while(t==x) { ++k;t=kth(root,k+1); } del(root,x); printf("%d\n",t); } } fclose(stdin); fclose(stdout); }
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; struct Node{ Node *ch[2]; int r,v,s,num; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();s=1;num=1;} int cmp(int x) const{ if (x==v) return -1; else return x<v?0:1; } void Maintain(){ s=num; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; } }; Node* root; int t; void rotate(Node* &o,int d) { Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o; o->Maintain();k->Maintain();o=k; } void insert(Node* &o,int x) { if (o==NULL) o=new Node(x); else { int d=o->cmp(x); if (d==-1) ++o->num; else { insert(o->ch[d],x); if (o->ch[d] ->r > o->r) rotate(o,d^1); } } o->Maintain(); } void del(Node* &o,int x) { int d=o->cmp(x); if (d==-1) { if (o->num==1) { if (o->ch[0]==NULL) o=o->ch[1]; else { if (o->ch[1]==NULL) o=o->ch[0]; else { int d2=(o->ch[0] ->r > o->ch[1] ->r ? 1 : 0); rotate(o,d2);del(o->ch[d2],x); } } } else --o->num; } else del(o->ch[d],x); if (o!=NULL) o->Maintain(); } int rank(Node* &o,int x) { int d=o->cmp(x); if (d==-1) return (o->ch[0]==NULL ? 1 : o->ch[0] ->s +1); else { if (d==0) return rank(o->ch[d],x); else return rank(o->ch[d],x) + (o->ch[0]==NULL ? o->num : o->ch[0] ->s +o->num); } } int kth(Node* &o,int x) { if (o==NULL||x<=0||x > o->s) return 0; int s=(o->ch[0]==NULL ? 0 : o->ch[0] ->s); if (x>=s+1&&x<=s+ o->num) return o->v; if (x<=s) return kth(o->ch[0],x); else return kth(o->ch[1],x-s- o->num); } void pre(Node* &o,int x) { if (o->v >=x) { if (o->ch[0]!=NULL) pre(o->ch[0],x); } else { t=max(t,o->v); if (o->ch[1]!=NULL) pre(o->ch[1],x); } } void succ(Node* &o,int x) { if (o->v <=x) { if (o->ch[1]!=NULL) succ(o->ch[1],x); } else { t=min(t,o->v); if (o->ch[0]!=NULL) succ(o->ch[0],x); } } int main() { freopen("phs.in","r",stdin); freopen("phs.out","w",stdout); int n,i,j,x; scanf("%d",&n); for (i=1;i<=n;++i) { scanf("%d%d",&j,&x); if (j==1) insert(root,x); if (j==2) del(root,x); if (j==3) { t=rank(root,x); printf("%d\n",t); } if (j==4) { t=kth(root,x); printf("%d\n",t); } if (j==5) { t=-2100000000; pre(root,x); printf("%d\n",t); } if (j==6) { t=2100000000; succ(root,x); printf("%d\n",t); } } fclose(stdin); fclose(stdout); }
cogs516 求和
题目大意:求一段区间和%p后>=k的最小值。
思路:前缀和数组,从前往后加到平衡树里,同时找以这个为终点的子段和的最优值,更新答案就可以了。
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; struct Node{ Node* ch[2]; int v,r; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();} int cmp(int x) { if (x==v) return -1; else return (x<v?0:1); } }; Node *root; int s[100001]={0},k,p,t; void rotate(Node* &o,int d) { Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;o=k; } void insert(Node* &o,int x) { if (o==NULL) o=new Node(x); else { int d=o->cmp(x); if (d!=-1) { insert(o->ch[d],x); if (o->ch[d] ->r > o->r) rotate(o,d^1); } } } void work(Node* &o,int x) { int d=o->cmp(x); int cha; cha=x- o->v; while(cha<0) cha+=p; if (cha>=k) { t=min(t,cha); if (o->ch[1]!=NULL) work(o->ch[1],x); } else if (o->ch[0]!=NULL) work(o->ch[0],x); } int main() { freopen("suma.in","r",stdin); freopen("suma.out","w",stdout); int i,j,n,ans; scanf("%d%d%d",&n,&k,&p); insert(root,0); for (i=1;i<=n;++i) { scanf("%d",&j); s[i]=(s[i-1]+j)%p; } ans=2100000000; for (i=1;i<=n;++i) { t=2100000000; work(root,s[i]); insert(root,s[i]); ans=min(ans,t); } printf("%d\n",ans); fclose(stdin); fclose(stdout); }
cogs1533 营业额统计
题目大意:统计n天的最小波动值总和。
思路:将每一天的营业额从前往后加入到treap里,同时找之前的与这一天营业额最接近的值,更新答案。(这道题的数据竟然有没有的情况,要自己补0。。。
if(scanf("%d",&m)==EOF) m=0;)
#include<cstdio> #include<iostream> #include<cstdlib> #include<cmath> using namespace std; struct Node{ Node* ch[2]; int v,r; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();} int cmp(int x) { if (x==v) return -1; else return (x<v?0:1); } }*root; int t; void rotate(Node* &o,int d) { Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;o=k; } void insert(Node* &o,int x) { if (o==NULL) o=new Node(x); else { int d=o->cmp(x); if (d!=-1) { insert(o->ch[d],x); if (o->ch[d] ->r > o->r) rotate(o,d^1); } } } void work(Node* &o,int x) { if (o==NULL) return; int d=o->cmp(x); if (d==-1) { t=0; return; } else { if (d==1) { t=min(t,abs(o->v - x)); if (o->ch[d]!=NULL) work(o->ch[d],x); } else { t=min(t,abs(o->v - x)); if (o->ch[d]!=NULL) work(o->ch[d],x); } } } int main() { freopen("turnover.in","r",stdin); freopen("turnover.out","w",stdout); int n,m,i,j,ans=0; scanf("%d",&n); for (i=1;i<=n;++i) { if(scanf("%d",&m)==EOF) m=0; t=2100000000; work(root,m); insert(root,m); if (i==1) ans=m; else ans+=t; } printf("%d\n",ans); fclose(stdin); fclose(stdout); }
cogs314 郁闷的出纳员
题目大意:对员工进行一系列操作,有添加新员工,给员工涨减工资,如果工资低于最低工资标准,就会永久的离开。
思路:运用treap,涨减工资就是暴力,数据佷人性,直接就过了。
正解是维护一个全局变量delta,每次涨减工资都更改delta的值,然后用splay维护原数列,每次找到刚刚比delta+最低工资标准大的那一个,将它旋转到根,把左孩子都删掉;如果用fhqtreap维护,就看是第几大,然后删掉之前的就可以了。
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; struct Node{ Node* ch[2]; int v,r,s,num; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();s=1;num=1;} int cmp(int x) { if (x==v) return -1; else return (x<v?0:1); } void Maintain() { s=num; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; } }*root; int le=0,minn; void rotate(Node* &o,int d) { Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o; o->Maintain();k->Maintain();o=k; } void insert(Node* &o,int x) { if (o==NULL) o=new Node(x); else { int d=o->cmp(x); if (d==-1) ++o->num; else { insert(o->ch[d],x); if (o->ch[d] ->r > o->r) rotate(o,d^1); } } o->Maintain(); } void up(Node* &o,int x) { o->v=o->v + x; if (o->ch[0]!=NULL) up(o->ch[0],x); if (o->ch[1]!=NULL) up(o->ch[1],x); } void down(Node* &o,int x) { o->v=o->v - x; while(o->v<minn) { le+=(o->ch[0]==NULL ? 0 : o->ch[0] ->s) + o->num; if (o->ch[1]!=NULL) o=o->ch[1]; else { o=NULL; return; } o->v=o->v - x; } if (o->ch[0]!=NULL) down(o->ch[0],x); if (o->ch[1]!=NULL) down(o->ch[1],x); o->Maintain(); } int kth(Node* &o,int x) { if (o==NULL||x<=0||x > o->s) return -1; int d=(o->ch[0]==NULL ? 0 : o->ch[0] ->s); if (x>=d+1&&x<=d+ o->num) return o->v; if (x<=d) return kth(o->ch[0],x); else return kth(o->ch[1],x-d- o->num); } int main() { freopen("cashier.in","r",stdin); freopen("cashier.out","w",stdout); int n,i,j,t; char ch; scanf("%d%d",&n,&minn); for (i=1;i<=n;++i) { scanf("%*c%c%d",&ch,&j); if (ch=='I') if (j>=minn) insert(root,j); if (ch=='A') up(root,j); if (ch=='S') down(root,j); if (ch=='F') { j=(root==NULL ? 0 : root->s - j +1); t=kth(root,j); printf("%d\n",t); } } printf("%d\n",le); fclose(stdin); fclose(stdout); }
cogs62 宠物收养所
题目大意:一家店有宠物有人,宠物多的时候就人选,人多的时候就宠物选,使二者的特征值尽量靠近,若特征值靠近的有两个,就选被选者特征值较小的那一个。
思路:赤裸裸的平衡树,如果树空就把树的种类赋成要加入的元素的种类;如果树不空,如果元素种类和树种类一致就insert,如果不一致就开始选择(又用了比较。。。的写法,插入、名次、前驱后继。。。)
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> using namespace std; struct Node{ Node* ch[2]; int v,r,s,num; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();s=num=1;} int cmp(int x) { if (x==v) return -1; else return (x<v?0:1); } int Maintain() { s=num; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; } }*root; void rotate(Node* &o,int d) { Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o; o->Maintain();k->Maintain();o=k; } void insert(Node* &o,int x) { if (o==NULL) o=new Node(x); else { int d=o->cmp(x); if (d==-1) ++o->num; else { insert(o->ch[d],x); if (o->ch[d] ->r > o->r) rotate(o,d^1); } } o->Maintain(); } void del(Node* &o,int x) { int d=o->cmp(x); if (d==-1) { if (o->num==1) { if (o->ch[0]==NULL) o=o->ch[1]; else { if (o->ch[1]==NULL) o=o->ch[0]; else { int d2=(o->ch[0] ->r > o->ch[1] ->r ? 1 : 0); rotate(o,d2);del(o->ch[d2],x); } } } else --o->num; } else del(o->ch[d],x); if (o!=NULL) o->Maintain(); } int rank(Node* &o,int x) { int d=o->cmp(x); if (d==-1) return (o->ch[0]==NULL?0:o->ch[0] ->s)+1; if (d==0) return rank(o->ch[d],x); if (d==1) return (o->ch[0]==NULL?0:o->ch[0]->s)+1+rank(o->ch[d],x); } int kth(Node* &o,int x) { if (o==NULL||x<=0||x>o->s) return -1; int d=(o->ch[0]==NULL?0:o->ch[0] ->s); if (x>=d+1&&x<=d+o->num) return o->v; if (x<=d) return kth(o->ch[0],x); else return kth(o->ch[1],x-d-1); } int main() { freopen("pet.in","r",stdin); freopen("pet.out","w",stdout); int i,n,a,b,kind,k,x,y,ans=0,minn,minj; scanf("%d",&n); for (i=1;i<=n;++i) { scanf("%d%d",&a,&b); if (root==NULL) { kind=a;insert(root,b); } else { if (a==kind) { insert(root,b); } else { if (root->s ==1) { ans=(ans+abs(b- root->v))%1000000; del(root,root->v); } else { minn=1<<31-1; minj=-1; insert(root,b); k=rank(root,b); x=kth(root,k-1); y=kth(root,k+1); if (x!=-1&&abs(x-b)<minn) { minj=x;minn=abs(x-b); } if (y!=-1&&abs(y-b)<minn) { minj=y;minn=abs(y-b); } ans=(ans+minn)%1000000; del(root,b); del(root,minj); } } } } printf("%d\n",ans); fclose(stdin); fclose(stdout); }
cogs1341 永无乡
题目大意:给出一个无向图,中间会加边,询问与x点相连的第k重要的点是什么。
思路:用到了并查集和平衡树。因为这里的相连包括间接相连,所以我们应该用并查集维护是否为联通块。每次加边的时候判断两点是否联通,如果联通,就将个数少的联通块并入个数多的联通块中(启发式合并?),这里的并其实就是insert每一个元素。然后名次一下就可以了。
一开始在各个网站上wa,后来才知道是读入字符的问题,因为数据中可能有多个空格或者回车。。。又用了while来读入,也是醉了。。。
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; struct Node{ Node* ch[2]; int v,r,s; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();s=1;} int cmp(int x) { if (x==v) return -1; else return(x<v?0:1); } void Maintain() { s=1; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; } }*root[100001]; int fa[100001]={0},imp[100001]={0}; int rool(int x) { if (fa[x]!=x) fa[x]=rool(fa[x]); return fa[x]; } void rotate(Node* &o,int d) { Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o; o->Maintain();k->Maintain();o=k; } void insert(Node* &o,int x) { if (o==NULL) o=new Node(x); else { int d=o->cmp(x); insert(o->ch[d],x); if (o->ch[d] ->r > o->r) rotate(o,d^1); } o->Maintain(); } int kth(Node* &o,int x) { if (o==NULL||x<=0||x > o->s) return -1; int d=(o->ch[0]==NULL ? 0 : o->ch[0] ->s); if (x==d+1) return o->v; if (x<=d) return kth(o->ch[0],x); else return kth(o->ch[1],x-d-1); } void Mergeo(Node* &o1,Node* &o2) { if (o1->ch[0]!=NULL) Mergeo(o1->ch[0],o2); if (o1->ch[1]!=NULL) Mergeo(o1->ch[1],o2); insert(o2,o1->v); } void Merge(int i,int j) { if (root[i]->s > root[j]->s) { Mergeo(root[j],root[i]); fa[j]=i; } else { Mergeo(root[i],root[j]); fa[i]=j; } } int main() { freopen("bzoj_2733.in","r",stdin); freopen("bzoj_2733.out","w",stdout); int n,m,i,j,q,r1,r2,a,b; char ch; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) { scanf("%d",&j); imp[j]=i;fa[i]=i; insert(root[i],j); } for (i=1;i<=m;++i) { scanf("%d%d",&j,&q); r1=rool(j);r2=rool(q); if (r1!=r2) Merge(r1,r2); } scanf("%d",&q); for (i=1;i<=q;++i) { while(true) { scanf("%c",&ch); if (ch=='B'||ch=='Q') break; } scanf("%d%d",&a,&b); if (ch=='B') { r1=rool(a);r2=rool(b); if (r1!=r2) Merge(r1,r2); } else { r1=rool(a); m=kth(root[r1],b); if (m==-1) printf("-1\n"); else printf("%d\n",imp[m]); } } fclose(stdin); fclose(stdout); }
poj3580 SuperMemo
题目大意:对一个数组进行插入、删除、区间加、区间平移、区间翻转、区间最值的操作,对于每个min操作输出最小值。
思路:为了这道题,学了很久的不需要旋转的treap,merge、split操作也是醉了,不过很神奇,也能解决很多东西。主要的过程就是拆分合并操作,能够拔一棵treap按要求分成两棵树。其他的操作都是在这两个操作的基础上各种下标、长度。。。自己写的东西很莫名,merge和split都会改变原来的树(因为这个一直wa,在求min操作的时候在拆分完了就要把最小值保存一下,不然合并之后就变了)。还有自己的merge递归的时候数组名不能变位置,不然就会错。这个题中,要用lazyi标记,分理处子树的时候写下标记,然后每次访问到这个点就要pushdown一下,还有可能改变子树的时候就要updata。最后要认真读题,不要自己凭空想出一些特殊的条件,要自己能找出各种反例。。。顺道练习了一下对拍。。。
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; struct Node{ Node* ch[2]; int r,s,num,minn,res,delta; Node(int num):num(num),minn(num) {ch[0]=ch[1]=NULL;r=rand();s=1;res=delta=0;} void updata() { s=1; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; minn=num; if (ch[0]!=NULL&&minn > ch[0]->minn + delta) minn=ch[0]->minn + delta; if (ch[1]!=NULL&&minn > ch[1]->minn + delta) minn=ch[1]->minn + delta; } void pushdown() { if (res) { if (ch[0]!=NULL) ch[0]->res=ch[0]->res ^ 1; if (ch[1]!=NULL) ch[1]->res=ch[1]->res ^ 1; swap(ch[0],ch[1]); res=0; } if (delta) { if (ch[0]!=NULL) { ch[0]->delta+=delta;ch[0]->num+=delta;ch[0]->minn+=delta; } if (ch[1]!=NULL) { ch[1]->delta+=delta;ch[1]->num+=delta;ch[1]->minn+=delta; } delta=0; } } }*root; char ch[10]; void dfs(Node* &o) { if (o==NULL) return; o->pushdown(); dfs(o->ch[0]); dfs(o->ch[1]); o->updata(); } Node *merge(Node* &aa,Node* &bb) { if (aa==NULL) return bb; if (bb==NULL) return aa; if (aa->r < bb->r) { if (aa!=NULL) aa->pushdown(); aa->ch[1]=merge(aa->ch[1],bb); if (aa!=NULL) aa->updata(); return aa; } else { if (bb!=NULL) bb->pushdown(); bb->ch[0]=merge(aa,bb->ch[0]); if (bb!=NULL) bb->updata(); return bb; } } void split(Node* &o,Node* &aa,Node* &bb,int x) { if (o==NULL) { aa=NULL;bb=NULL;return; } if (x==0) { aa=NULL;bb=o; return; } if (x==o->s) { aa=o;bb=NULL; return; } o->pushdown(); if ((o->ch[0]==NULL?0:o->ch[0] ->s)>=x) { split(o->ch[0],aa,bb,x); o->ch[0]=bb; o->updata(); bb=o; } else { split(o->ch[1],aa,bb,x-(o->ch[0]==NULL?0:o->ch[0] ->s)-1); o->ch[1]=aa; o->updata(); aa=o; } } void insert(Node* &o,int x,int i) { Node *p,*y,*z; if (o==NULL) o=new Node(x); else { p=new Node(x); split(o,y,z,i-1); p=merge(p,z); o=merge(y,p); } } void del(Node* &o,int i) { Node *x,*y,*z,*p; split(o,x,y,i-1); split(y,p,z,1); o=merge(x,z); } void add(Node* &o,int x,int y,int a) { Node *p,*q,*k,*z; split(o,p,q,x-1); split(q,z,k,y-x+1); if (z!=NULL) {z->delta=z->delta+a;z->minn+=a;z->num+=a;} z=merge(z,k); o=merge(p,z); } void reverse(Node* &o,int x,int y) { Node *p,*q,*k,*z; split(o,p,q,x-1); split(q,z,k,y-x+1); if (z!=NULL) z->res=z->res^1; z=merge(z,k); o=merge(p,z); } void revolve(Node* &o,int x,int y,int a) { Node *p,*q,*k,*j,*z,*b; split(o,p,q,x-1); split(q,j,k,y-x+1); split(j,z,b,y-x+1-a); j=merge(b,z); q=merge(j,k); o=merge(p,q); } int qmin(Node* &o,int x,int y) { Node *p,*q,*k,*z; int ans; split(o,p,q,x-1); split(q,z,k,y-x+1); ans=z->minn; q=merge(z,k); o=merge(p,q); return ans; } int main() { int i,j,n,m,x,y,u; scanf("%d",&n); for (i=1;i<=n;++i) { scanf("%d",&x); insert(root,x,i); } scanf("%d",&m); for (i=1;i<=m;++i) { scanf("%*c%s",&ch); if (ch[0]=='A') { scanf("%d%d%d",&x,&y,&u); add(root,x,y,u); } if (ch[0]=='R'&&ch[3]=='E') { scanf("%d%d",&x,&y); reverse(root,x,y); } if (ch[0]=='R'&&ch[3]=='O') { scanf("%d%d%d",&x,&y,&u); u=u%(y-x+1); if (u<0) u=u+y-x+1; u=u%(y-x+1); revolve(root,x,y,u); } if (ch[0]=='I') { scanf("%d%d",&y,&x); insert(root,x,y+1); } if (ch[0]=='D') { scanf("%d",&x); del(root,x); } if (ch[0]=='M') { scanf("%d%d",&x,&y); u=qmin(root,x,y); printf("%d\n",u); } } }
cogs330 文本编辑器--bzoj1269 文本编辑器
题目大意:对字符串进行插入、删除、光标、翻转操作。
思路:明显模板题,水过。。。但是一开始T了好久,插入的时候不要命的一个字母一个字母的split、merge。。。忧桑。。。
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; struct Node{ Node* ch[2]; int r,s; char v; Node(char v):v(v) {ch[0]=ch[1]=NULL;r=rand();s=1;} void updata() { s=1; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; } }*root; char ch[10]; Node *merge(Node* &aa,Node* &bb) { if (aa==NULL) return bb; if (bb==NULL) return aa; if (aa->r < bb->r) { aa->ch[1]=merge(aa->ch[1],bb); if (aa!=NULL) aa->updata(); return aa; } else { bb->ch[0]=merge(aa,bb->ch[0]); if (bb!=NULL) bb->updata(); return bb; } } void split(Node* &o,Node* &aa,Node* &bb,int x) { if (o==NULL) { aa=bb=NULL;return; } if (x==0) { aa=NULL;bb=o;return; } if (x==o->s) { aa=o;bb=NULL;return; } if ((o->ch[0]==NULL?0:o->ch[0] ->s)>=x) { split(o->ch[0],aa,bb,x); o->ch[0]=bb; o->updata(); bb=o; } else { split(o->ch[1],aa,bb,x-(o->ch[0]==NULL?0:o->ch[0] ->s)-1); o->ch[1]=aa; o->updata(); aa=o; } } void insert(Node* &o,char x,int i) { if (o==NULL) o=new Node(x); else { Node *p=new Node(x); Node *y,*z,*q; split(o,y,z,i); q=merge(y,p); o=merge(q,z); } } void del(Node* &o,int l,int i) { Node *p,*q,*y,*z; split(o,p,q,i); split(q,y,z,l); o=merge(p,z); } void dfs(Node* &o) { if (o==NULL) return; dfs(o->ch[0]); printf("%c",o->v); dfs(o->ch[1]); } void get(Node* &o,int i,int x) { Node *p,*q,*y,*z; split(o,p,q,i); split(q,y,z,x); dfs(y);printf("\n"); q=merge(y,z); o=merge(p,q); } int main() { int n,i,j,k,m; char ss; scanf("%d",&n); k=0; for (i=1;i<=n;++i) { scanf("%*c%s",ch); if (ch[0]=='M') scanf("%d",&k); if (ch[0]=='I') { scanf("%d",&j); for (m=1;m<=j;++m) { while(true) { scanf("%c",&ss); if (ss>=32&&ss<=126) break; } insert(root,ss,k+m-1); } } if (ch[0]=='D') { scanf("%d",&j); del(root,j,k); } if (ch[0]=='G') { scanf("%d",&j); get(root,k,j); } if (ch[0]=='P') --k; if (ch[0]=='N') ++k; } }
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; struct Node{ Node* ch[2]; int r,s,res; char v; Node(char v):v(v) {ch[0]=ch[1]=NULL;r=rand();s=1;res=0;} void updata() { s=1; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; } void pushdown() { if (res) { if (ch[0]!=NULL) ch[0]->res ^=1; if (ch[1]!=NULL) ch[1]->res ^=1; swap(ch[0],ch[1]); res=0; } } }*root; char ch[10]; Node *merge(Node* aa,Node* bb) { if (aa==NULL) return bb; if (bb==NULL) return aa; if (aa->r < bb->r) { if (aa!=NULL) aa->pushdown(); aa->ch[1]=merge(aa->ch[1],bb); if (aa!=NULL) aa->updata(); return aa; } else { if (bb!=NULL) bb->pushdown(); bb->ch[0]=merge(aa,bb->ch[0]); if (bb!=NULL) bb->updata(); return bb; } } void split(Node* o,Node* &aa,Node* &bb,int x) { if (o==NULL) { aa=bb=NULL;return; } if (x==0) { aa=NULL;bb=o;return; } if (x==o->s) { aa=o;bb=NULL;return; } o->pushdown(); if ((o->ch[0]==NULL?0:o->ch[0] ->s)>=x) { split(o->ch[0],aa,bb,x); o->ch[0]=bb; o->updata(); bb=o; } else { split(o->ch[1],aa,bb,x-(o->ch[0]==NULL?0:o->ch[0] ->s)-1); o->ch[1]=aa; o->updata(); aa=o; } } void insert(Node* &o,int x,int i) { Node *p; int m; char ss; Node *y,*z,*q; split(o,y,z,x); for (m=1;m<=i;++m) { while(true) { scanf("%c",&ss); if (ss>=32&&ss<=126) break; } p=new Node(ss); y=merge(y,p); } o=merge(y,z); } void del(Node* &o,int l,int i) { Node *p,*q,*y,*z; split(o,p,q,i); split(q,y,z,l); o=merge(p,z); } void get(Node* &o,int i,int x) { Node *p,*q,*y,*z; split(o,p,q,i); split(q,y,z,x); printf("%c\n",y->v); q=merge(y,z); o=merge(p,q); } void rotate(Node* &o,int i,int x) { Node *p,*q,*y,*z; split(o,p,q,i); split(q,y,z,x); if (y!=NULL) y->res ^=1; q=merge(y,z); o=merge(p,q); } int main() { int n,i,j,k,m; scanf("%d",&n); k=0; for (i=1;i<=n;++i) { scanf("%*c%s",ch); if (ch[0]=='M') scanf("%d",&k); if (ch[0]=='I') { scanf("%d",&j); insert(root,k,j); } if (ch[0]=='D') { scanf("%d",&j); del(root,j,k); } if (ch[0]=='G') get(root,k,1); if (ch[0]=='P') --k; if (ch[0]=='N') ++k; if (ch[0]=='R') { scanf("%d",&j); rotate(root,k,j); } } }
cogs659 报表统计
题目大意:给出n个元素,然后支持插入(插入到给定的n个元素的相应位置的最后)、查询最小绝对值差(要支持相邻元素和全局两种查询)。
思路:一开始想用树套树之类的东西,可惜不会写。于是就开始找些巧(ben)妙(zhuo)地方法。可以保存一下原数列以及每个元素对应的插入的最后一个元素。然后建两棵treap,一棵用来保存这些插入的元素,利用前驱后继找最小绝对值差中全局的查询;另一棵中保存的是相邻元素的差,这时候之前的两个数组就起作用了,对于一个要插入的元素,它破坏了一个相邻关系,又建立了两个相邻关系,这时候在树中进行插入、删除就可以了。食府之高。。。可想而知。不过时限很宽,就水过了。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> using namespace std; struct Node{ Node* ch[2]; int v,r,num; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();num=1;} int cmp(int x) { if (x==v) return -1; else return (x<v?0:1); } }*root1,*root2; int a[500001]={0},b[500001]={0},j; char ch[20]; void rotate(Node* &o,int d) { Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;o=k; } void insert(Node* &o,int x) { if (o==NULL) o=new Node(x); else { int d=o->cmp(x); if (d==-1) ++o->num; else { insert(o->ch[d],x); if (o->ch[d] ->r > o->r) rotate(o,d^1); } } } void del(Node* &o,int x) { int d=o->cmp(x); if (d==-1) { if (o->num==1) { if (o->ch[0]==NULL) o=o->ch[1]; else { if (o->ch[1]==NULL) o=o->ch[0]; else { int d2=(o->ch[0] ->r > o->ch[1] ->r ?1:0); rotate(o,d2);del(o->ch[d2],x); } } } else --o->num; } else del(o->ch[d],x); } void pre(Node* &o,int x) { if (o==NULL) return; int d=o->cmp(x); if (d==-1) j=o->v; else { if (d==1) j=max(j,o->v); pre(o->ch[d],x); } } void succ(Node* &o,int x) { if (o==NULL) return; int d=o->cmp(x); if (d==-1) j=o->v; else { if (d==0) j=min(j,o->v); succ(o->ch[d],x); } } int qmin(Node* &o) { if (o->ch[0]==NULL) return o->v; else return qmin(o->ch[0]); } int main() { freopen("form.in","r",stdin); freopen("form.out","w",stdout); int n,m,i,k,ans; scanf("%d%d",&n,&m); ans=2100000000; for (i=1;i<=n;++i) { scanf("%d",&a[i]); b[i]=a[i]; if (i>1) insert(root2,abs(a[i]-a[i-1])); j=-1000000000; pre(root1,a[i]); if (a[i]-j<ans) ans=a[i]-j; j=1000000000; succ(root1,a[i]); if (j-a[i]<ans) ans=j-a[i]; insert(root1,a[i]); } for (i=1;i<=m;++i) { scanf("%*c%s",&ch); if (ch[0]=='I') { scanf("%d%d",&j,&k); del(root2,abs(b[j]-a[j+1])); insert(root2,abs(k-b[j])); insert(root2,abs(k-a[j+1])); b[j]=k; j=-1000000000; pre(root1,k); if (k-j<ans) ans=k-j; j=1000000000; succ(root1,k); if (j-k<ans) ans=j-k; insert(root1,k); } if (ch[0]=='M') { if (ch[4]=='S') printf("%d\n",ans); else { k=qmin(root2); printf("%d\n",k); } } } fclose(stdin); fclose(stdout); }
其实这道题也可以用堆来完成找邻近最小差的操作,时间会快,不过离dada神的2s+还是有距离。。。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; struct Node{ Node* ch[2]; int v,r,num; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();num=1;} int cmp(int x) { if (x==v) return -1; else return (x<v?0:1); } }*root1; int a[500001]={0},b[500001]={0},j,heap[4000001]={0},se[4000001]={0},cha[4000001][2]={0},tot=0; char ch[20]; void rotate(Node* &o,int d) { Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;o=k; } void insert(Node* &o,int x) { if (o==NULL) o=new Node(x); else { int d=o->cmp(x); if (d==-1) ++o->num; else { insert(o->ch[d],x); if (o->ch[d] ->r > o->r) rotate(o,d^1); } } } void pre(Node* &o,int x) { if (o==NULL) return; int d=o->cmp(x); if (d==-1) j=o->v; else { if (d==1) j=max(j,o->v); pre(o->ch[d],x); } } void succ(Node* &o,int x) { if (o==NULL) return; int d=o->cmp(x); if (d==-1) j=o->v; else { if (d==0) j=min(j,o->v); succ(o->ch[d],x); } } void up(int x) { int i; while (x>1) { i=x/2; if (cha[heap[x]][0]<cha[heap[i]][0]) { swap(cha[heap[x]][1],cha[heap[i]][1]); swap(heap[x],heap[i]); } x=i; } } void down(int x) { int i; while(x*2<=tot) { if (x*2==tot||(x*2<tot&&cha[heap[x*2]][0]<cha[heap[x*2+1]][0])) i=x*2; else i=x*2+1; if (cha[heap[x]][0]>cha[heap[i]][0]) { swap(cha[heap[x]][1],cha[heap[i]][1]); swap(heap[x],heap[i]); } else return; x=i; } } int main() { freopen("form.in","r",stdin); freopen("form.out","w",stdout); int n,m,i,k,ans; scanf("%d%d",&n,&m); ans=2100000000; scanf("%d",&a[1]);insert(root1,a[1]);b[1]=a[1]; for (i=2;i<=n;++i) { scanf("%d",&a[i]); b[i]=a[i]; ++tot;cha[tot][0]=abs(a[i]-a[i-1]);cha[tot][1]=tot;heap[tot]=tot; se[i-1]=tot;up(tot); j=-1000000000; pre(root1,a[i]); if (a[i]-j<ans) ans=a[i]-j; j=1000000000; succ(root1,a[i]); if (j-a[i]<ans) ans=j-a[i]; insert(root1,a[i]); } for (i=1;i<=m;++i) { scanf("%*c%s",&ch); if (ch[0]=='I') { scanf("%d%d",&j,&k); if (j<n) { cha[se[j]][0]=2100000000;down(cha[se[j]][1]); ++tot;cha[tot][0]=abs(k-b[j]);cha[tot][1]=tot;heap[tot]=tot;up(tot); ++tot;cha[tot][0]=abs(a[j+1]-k);cha[tot][1]=tot;heap[tot]=tot;se[j]=tot;up(tot); } else { ++tot;cha[tot][0]=abs(k-b[j]);cha[tot][1]=tot;heap[tot]=tot;up(tot); } b[j]=k; j=-1000000000; pre(root1,k); if (k-j<ans) ans=k-j; j=1000000000; succ(root1,k); if (j-k<ans) ans=j-k; insert(root1,k); } if (ch[0]=='M') { if (ch[4]=='S') printf("%d\n",ans); else { k=cha[heap[1]][0]; printf("%d\n",k); } } } fclose(stdin); fclose(stdout); }
cogs339||bzoj1500 维护(修)数列
题目大意:支持插入、删除、翻转、区间覆盖、区间求和、求最大连续子序列和。
思路:这题坑了我一上午。。。醉了。磕磕绊绊的写完了treap,然后就是无限调试。这道题的前五个操作都是很普通的,只有最后一个操作有些特异(其实也比较简单,有山海经做铺垫),每次更新这个结点从左面和右面拓展的最大和以及真个结点的最大和,updata超长更新。。。对于pushdown中的翻转,应该放到孙子级,否则可能会出现lmax、rmax未更新的问题。然后就是bzoj的坑爹内存范围了。要删点否则就会mle,可是又不会垃圾回收,只能听天由命(加上限的递归删点。。。)
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; #define star -2100000000 struct Node{ Node* ch[2]; int v,r,s,sum,lmax,rmax,maxn,delta,rev; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();s=1;sum=lmax=rmax=maxn=v;rev=0;delta=star;} void pushdown() { if (rev) { if (ch[0]!=NULL) { ch[0]->rev ^=1; swap(ch[0]->ch[0],ch[0]->ch[1]); swap(ch[0]->lmax,ch[0]->rmax); } if (ch[1]!=NULL) { ch[1]->rev ^=1; swap(ch[1]->ch[0],ch[1]->ch[1]); swap(ch[1]->lmax,ch[1]->rmax); } rev=0; } if (delta!=star) { if (ch[0]!=NULL) { ch[0]->v=delta;ch[0]->sum=ch[0]->s*delta; ch[0]->lmax=ch[0]->rmax=ch[0]->maxn=max(delta,delta*ch[0]->s); ch[0]->delta=delta; } if (ch[1]!=NULL) { ch[1]->v=delta;ch[1]->sum=ch[1]->s*delta; ch[1]->lmax=ch[1]->rmax=ch[1]->maxn=max(delta,delta*ch[1]->s); ch[1]->delta=delta; } delta=star; } } void updata() { s=1; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; sum=v; if (ch[0]!=NULL) sum+=ch[0]->sum; if (ch[1]!=NULL) sum+=ch[1]->sum; if (ch[0]!=NULL) lmax=max(ch[0]->lmax,ch[0]->sum+v+max(0,(ch[1]==NULL?0:ch[1]->lmax))); else lmax=v+max(0,(ch[1]==NULL?0:ch[1]->lmax)); if (ch[1]!=NULL) rmax=max(ch[1]->rmax,ch[1]->sum+v+max(0,(ch[0]==NULL?0:ch[0]->rmax))); else rmax=v+max(0,(ch[0]==NULL?0:ch[0]->rmax)); maxn=v; if (ch[0]!=NULL) maxn=max(maxn,maxn+ch[0]->rmax); if (ch[1]!=NULL) maxn=max(maxn,maxn+ch[1]->lmax); if (ch[0]!=NULL) maxn=max(maxn,ch[0]->maxn); if (ch[1]!=NULL) maxn=max(maxn,ch[1]->maxn); maxn=max(maxn,max(lmax,rmax)); } }*root; char ch[10]; int lll=0; void dfs(Node* o) { if (o==NULL) return; o->pushdown(); dfs(o->ch[0]); printf("%d ",o->v); dfs(o->ch[1]); o->updata(); } Node* merge(Node* aa,Node* bb) { if (aa==NULL) return bb; if (bb==NULL) return aa; if (aa->r < bb->r) { if (aa!=NULL) aa->pushdown(); aa->ch[1]=merge(aa->ch[1],bb); if (aa!=NULL) aa->updata(); return aa; } else { if (bb!=NULL) bb->pushdown(); bb->ch[0]=merge(aa,bb->ch[0]); if (bb!=NULL) bb->updata(); return bb; } } void split(Node* &o,Node* &aa,Node* &bb,int x) { if (o==NULL) { aa=bb=NULL; return; } o->pushdown(); if (x==0) { aa=NULL;bb=o; return; } if (x==o->s) { aa=o;bb=NULL; return; } if ((o->ch[0]==NULL ? 0 : o->ch[0] ->s)>=x) { split(o->ch[0],aa,bb,x); o->ch[0]=bb; o->updata(); bb=o; } else { split(o->ch[1],aa,bb,x-(o->ch[0]==NULL ? 0 : o->ch[0] ->s)-1); o->ch[1]=aa; o->updata(); aa=o; } } void insert(Node* &o,int x,int j) { int i,n; Node *p,*q,*y,*z; if (o==NULL) { for (i=1;i<=j;++i) { scanf("%d",&n); y=new Node(n); z=merge(o,y); o=z; } return; } split(o,p,q,x); for (i=1;i<=j;++i) { scanf("%d",&n); y=new Node(n); z=merge(p,y); p=z; } o=merge(p,q); } void era(Node* &o) { if (o==NULL) return; era(o->ch[0]); era(o->ch[1]); delete o;o=NULL; } void del(Node* &o,int x,int j) { Node *p,*q,*y,*z; split(o,p,q,x-1); split(q,y,z,j); ++lll; if (lll<=500) era(y); o=merge(p,z); } void make(Node* &o,int x,int j,int c) { Node *p,*q,*y,*z; split(o,p,q,x-1); split(q,y,z,j); if (y!=NULL) { y->v=y->delta=c;y->sum=y->s*c; y->lmax=y->rmax=y->maxn=max(c,c* y->s); } q=merge(y,z); o=merge(p,q); } void reverse(Node* &o,int x,int j) { Node *p,*q,*y,*z; split(o,p,q,x-1); split(q,y,z,j); if (y!=NULL) { y->rev ^=1; swap(y->lmax,y->rmax); swap(y->ch[0],y->ch[1]); } q=merge(y,z); o=merge(p,q); } int get(Node* &o,int x,int j) { Node *p,*q,*y,*z; int k; split(o,p,q,x-1); split(q,y,z,j); k=(y==NULL?0:y->sum); q=merge(y,z); o=merge(p,q); return k; } int main() { freopen("seq2005.in","r",stdin); freopen("seq2005.out","w",stdout); int n,m,i,j,x,c; scanf("%d%d",&n,&m); insert(root,0,n); for (i=1;i<=m;++i) { scanf("%*c%s",&ch); if (ch[0]=='I') { scanf("%d%d",&x,&j); insert(root,x,j); } if (ch[0]=='D') { scanf("%d%d",&x,&j); del(root,x,j); } if (ch[0]=='M'&&ch[2]=='K') { scanf("%d%d%d",&x,&j,&c); make(root,x,j,c); } if (ch[0]=='R') { scanf("%d%d",&x,&j); reverse(root,x,j); } if (ch[0]=='G') { scanf("%d%d",&x,&j); c=get(root,x,j); printf("%d\n",c); } if (ch[0]=='M'&&ch[2]=='X') { printf("%d\n",root->maxn); } } fclose(stdin); fclose(stdout); }
cogs17||bzoj1493 项链工厂
题目大意:一个环形的项链,有很多不同颜色的珠子,支持平移、轴对称(翻转)、交换、区间染色、查询有多少段不连续的颜色(不同的区间)。
思路:颓了两周文化课之后的第一题,比较裸的treap,保存一下区间的左右端点颜色和有多少段不连续的颜色,然后用标记和updata更新就可以了,各种split和merge搞定了每一个操作。比较顺利的完成了code。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; struct Node{ Node* ch[2]; int v,r,s,sum,lc,rc,rev,delta; Node(int v):v(v) {ch[0]=ch[1]=NULL;r=rand();sum=s=1;lc=rc=v;rev=delta=0;} void pushdown() { if (rev) { if (ch[0]!=NULL) { ch[0]->rev ^=1; swap(ch[0]->ch[0],ch[0]->ch[1]); swap(ch[0]->lc,ch[0]->rc); } if (ch[1]!=NULL) { ch[1]->rev ^=1; swap(ch[1]->ch[0],ch[1]->ch[1]); swap(ch[1]->lc,ch[1]->rc); } rev=0; } if (delta) { if (ch[0]!=NULL) { ch[0]->delta=ch[0]->lc=ch[0]->rc=ch[0]->v=delta;ch[0]->sum=1; } if (ch[1]!=NULL) { ch[1]->delta=ch[1]->lc=ch[1]->rc=ch[1]->v=delta;ch[1]->sum=1; } delta=0; } } void updata() { s=1; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; sum=1; if (ch[0]!=NULL) { sum+=ch[0]->sum; if (v == ch[0]->rc) --sum; } if (ch[1]!=NULL) { sum+=ch[1]->sum; if (v == ch[1]->lc) --sum; } lc=(ch[0]==NULL?v:ch[0]->lc); rc=(ch[1]==NULL?v:ch[1]->rc); } }*root; char cha[3]; void dfs(Node* &o) { if (o==NULL) return; o->pushdown(); dfs(o->ch[0]); printf("%d ",o->v); dfs(o->ch[1]); o->updata(); } void split(Node* &o,Node* &aa,Node* &bb,int k) { if (o==NULL) { aa=bb=NULL;return; } if (k==0) { aa=NULL;bb=o;return; } if (k==o->s) { aa=o;bb=NULL;return; } o->pushdown(); if ((o->ch[0]==NULL?0: o->ch[0] ->s) >=k) { split(o->ch[0],aa,bb,k); o->ch[0]=bb; o->updata(); bb=o; } else { split(o->ch[1],aa,bb,k-(o->ch[0]==NULL?0 : o->ch[0] ->s)-1); o->ch[1]=aa; o->updata(); aa=o; } } Node* merge(Node *aa,Node *bb) { if (aa==NULL) return bb; if (bb==NULL) return aa; if (aa->r < bb->r) { if (aa!=NULL) aa->pushdown(); aa->ch[1]=merge(aa->ch[1],bb); if (aa!=NULL) aa->updata(); return aa; } else { if (bb!=NULL) bb->pushdown(); bb->ch[0]=merge(aa,bb->ch[0]); if (bb!=NULL) bb->updata(); return bb; } } void revolve(Node* &o,int k) { Node *p,*q; split(o,p,q,o->s - k); o=merge(q,p); } void reverse(Node* &o,int i,int j) { Node *p,*q; split(o,p,q,i-1); if (q!=NULL) { swap(q->ch[0],q->ch[1]); swap(q->lc,q->rc); q->rev ^=1; } o=merge(p,q); } void swa(Node* &o,int i,int j) { Node *p,*q,*y,*z,*a,*b,*c,*d; split(o,p,q,i-1); split(q,y,z,1); split(z,a,b,j-i-1); split(b,c,d,1); b=merge(y,d); z=merge(a,b); q=merge(c,z); o=merge(p,q); } void paint(Node* &o,int i,int j,int x) { Node *p,*q,*y,*z; if (i<=j) { split(o,p,q,i-1); split(q,y,z,j-i+1); if (y!=NULL) { y->delta=x; y->sum=1;y->lc=y->rc=y->v=x; } q=merge(y,z); o=merge(p,q); } else { split(o,p,q,j); split(q,y,z,i-j-1); if (p!=NULL) { p->delta=x;p->sum=1;p->lc=p->rc=p->v=x; } if (z!=NULL) { z->delta=x;z->sum=1;z->lc=z->rc=z->v=x; } q=merge(y,z); o=merge(p,q); } } int count(Node* &o,int i,int j) { Node *p,*q,*y,*z; int ans=0; if (i<=j) { split(o,p,q,i-1); split(q,y,z,j-i+1); ans=(y==NULL?0:y->sum); q=merge(y,z); o=merge(p,q); return ans; } else { split(o,p,q,j); split(q,y,z,i-j-1); ans+=(p==NULL?0: p->sum)+(z==NULL?0: z->sum); if (p!=NULL&&z!=NULL&&p->lc == z->rc&&ans>1) --ans; q=merge(y,z); o=merge(p,q); return ans; } } int main() { freopen("necklace.in","r",stdin); freopen("necklace.out","w",stdout); int n,c,xi,i,j,q,x,ans; Node *p; scanf("%d%d",&n,&c); for (i=1;i<=n;++i) { scanf("%d",&xi); p=new Node(xi); root=merge(root,p); } scanf("%d",&q); for (i=1;i<=q;++i) { ans=x=0; scanf("%*c%s",&cha); if (cha[0]=='R') { scanf("%d",&j); revolve(root,j); } if (cha[0]=='F') reverse(root,2,n); if (cha[0]=='S') { scanf("%d%d",&j,&xi); if (j==xi) continue; if (j>xi) swap(j,xi); swa(root,j,xi); } if (cha[0]=='P') { scanf("%d%d%d",&j,&xi,&x); paint(root,j,xi,x); } if (cha[0]=='C'&&strlen(cha)==1) { ans=root->sum; if (root->lc == root->rc && ans>1) --ans; printf("%d\n",ans); } if (cha[0]=='C'&&strlen(cha)==2) { scanf("%d%d",&j,&xi); x=count(root,j,xi); printf("%d\n",x); } } fclose(stdin); fclose(stdout); }
cogs1545||bzoj1552||bzoj3506 机器排序
题目大意:有一堆的样品,不同高度,每次都找到无序的当中最矮的,到无序的开头这一段翻转,以此类推,做n次后排成升序,每次输出有序位置上的样品在最近一次翻转前的位置,cogs上是多组,bzoj上分成了很多点。
思路:思路当然是用treap很开心的放标记,reverse就可以了,但是很多棘手的问题。。。比如题目描述中要求相同高度的时候要求原序列靠前的尽量靠前(这里的原序列是最早的输入序列),对于这个问题,用fye大神的思路,对原数列排序(位置作为第二关键字),然后给相应的位置附上值,使他们都是不重复的元素(相当于附上了重要值),这样就减小了code复杂度;然后就是最重要的treap部分了,一开始的思路是保存每一个节点的下标,交换的时候,更改下标就醉了,于是转换思路,我们是不是可以不保存下标,每次都去找这个最小值的下标,在treap里可以新开一个指针,指向这个区间内的最小值,然后要找这个最小值得位置的时候只需要从根结点一点一点地往下找(先pushdown,然后从左右儿子中找谁的最小值是这个,如果是右儿子就要把排名加上左儿子中元素个数+1,找到这个点后,不要忘了再加一遍左儿子的个数+1),这样的复杂度也不是很高,在treap里新开的minn指针也是一个很好地方法,同时在指针里,minn=this就是指向这个结点的意思。学了好多新东西!!!
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> using namespace std; struct Node{ Node *ch[2],*minn; int v,r,s,rev; Node(int v):v(v) {ch[0]=ch[1]=NULL;minn=this;r=rand();s=1;rev=0;} void pushdown() { if (rev) { if (ch[0]!=NULL) { ch[0]->rev ^=1; swap(ch[0]->ch[0],ch[0]->ch[1]); } if (ch[1]!=NULL) { ch[1]->rev ^=1; swap(ch[1]->ch[0],ch[1]->ch[1]); } rev=0; } } void updata() { s=1; if (ch[0]!=NULL) s+=ch[0]->s; if (ch[1]!=NULL) s+=ch[1]->s; minn=this; if (ch[0]!=NULL&&ch[0]->minn->v < minn->v) minn=ch[0]->minn; if (ch[1]!=NULL&&ch[1]->minn->v < minn->v) minn=ch[1]->minn; } }*root; struct use{ int num,pos; }a[100001]; int b[100001]={0}; int my_comp(const use &x,const use &y) { if (x.num<y.num) return 1; if (x.num==y.num&&x.pos<y.pos) return 1; return 0; } Node* merge(Node *aa,Node *bb) { if (aa==NULL) return bb; if (bb==NULL) return aa; if (aa->r < bb->r) { if (aa!=NULL) aa->pushdown(); aa->ch[1]=merge(aa->ch[1],bb); if (aa!=NULL) aa->updata(); return aa; } else { if (bb!=NULL) bb->pushdown(); bb->ch[0]=merge(aa,bb->ch[0]); if (bb!=NULL) bb->updata(); return bb; } } void split(Node* &o,Node* &aa,Node* &bb,int k) { if (o==NULL) { aa=bb=NULL;return; } if (k==0) { aa=NULL;bb=o;return; } if (k==o->s) { aa=o;bb=NULL;return; } o->pushdown(); if ((o->ch[0]==NULL?0:o->ch[0] ->s) >=k) { split(o->ch[0],aa,bb,k); o->ch[0]=bb; o->updata(); bb=o; } else { split(o->ch[1],aa,bb,k- (o->ch[0]==NULL?0:o->ch[0] ->s) -1); o->ch[1]=aa; o->updata(); aa=o; } } void work(Node* &o,int i) { Node *p,*q,*y,*z; int t=0; q=o; while(q->minn !=q) { q->pushdown(); if (q->ch[0]!=NULL&&q->ch[0] ->minn ->v==q->minn ->v) q=q->ch[0]; else { t+=(q->ch[0]==NULL?0:q->ch[0] ->s)+1; q=q->ch[1]; } } t+=(q->ch[0]==NULL?0:q->ch[0] ->s)+1; printf("%d ",t+i-1); split(o,y,z,t); if (y!=NULL) { y->rev ^=1; swap(y->ch[0],y->ch[1]); } q=merge(y,z); split(q,p,o,1); } int main() { int n,i,j; Node *q; scanf("%d",&n); for (i=1;i<=n;++i) { scanf("%d",&a[i].num); a[i].pos=i; } sort(a+1,a+n+1,my_comp); for (i=1;i<=n;++i) b[a[i].pos]=i; for (i=1;i<=n;++i) { q=new Node(b[i]); root=merge(root,q); } for (i=1;i<n;++i) work(root,i); printf("%d\n",n); }
省队集训day2T3
题目大意:有一个看似随机的数列a(是由(1023^i(%1e9))xor(1025^i(%1e9))得到的),给出l1,r1,l2,r2,求sigma(max(ai~j)-min(ai~j))(l1<=i<=r1,max(i,l2)<=j<=r2)。
思路:这个数列a是为了构造随机的数列(做题的时候一直认为这是什么数论之类的,听题解的时候才明白,这只是为了得到一个随机的数列,要积累这一类的经验。)然后我们的任务就是统计两端区间x、y,起点在x中、终点在y中的区间中最大最小值的差,根据sigma的式子,我们可以将max和min拆开来计算。以最大值为例,我们学习一下怎么求这个答案:对于一个区间[l,r],最大值r的位置为po,那么这一段的答案就是ans[l,po-1]+ans[po+1,r]+r*(lson_siz+1)*(rson_siz+1)(这里一定要注意+1,因为有以这个点为边界的区间也要更新答案,所以我们在后面叶节点的初值中就要赋为r)。接下来我们考虑两个区间的答案怎么更新:(1)两个不相交的区间,同时1在2前,ans=ans[l1,r2]-ans[l1,l2-1]-ans[r1+1,r2]+ans[r1+1,l2-1];(2)两个相交的区间,ans=ans[l1,r2]-ans[l1,l2-1]-ans[r1+1,r2];(3)两个包含的区间,ans=ans[l1,r2]-ans[l1,l2-1];(4)对于l2<l1的情况,我们只需要l2=l1就可以了(之前的部分我们不需要);(5)我们特判出ans=0的情况,r2<l1。然后我们对于ans的维护,就可以用FHQtreep来维护,其中的rand值就是a数组,大小的值就是下标,正好迎合了之前强调的a数列是个看似随机的数列这一点。这样我们就可以愉快的ac了。
注意:(1)这种根据连续区间更新不连续区间的方法要掌握;(2)要注意细节,对于很多split出来的部分可能为NULL,要判出;(3)要通过题目积累经验,把平时学习的数据结构、算法等的特点熟练的和题目中的特点相结合。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define P 1000000000LL #define maxnode 100005 using namespace std; struct Node{ Node* ch[2]; int sz,r,num; long long ans; Node(int num,int r):num(num),r(r){ans=r;sz=1;ch[0]=ch[1]=NULL;}//ans的初值为r,不是0. void updata() { sz=1;ans=0; if (ch[0]!=NULL) { sz+=ch[0]->sz;ans+=ch[0]->ans; } if (ch[1]!=NULL) { sz+=ch[1]->sz;ans+=ch[1]->ans; } ans+=(long long)r*(((long long)(ch[0]!=NULL ? ch[0]->sz : 0)+1)* (long long)((ch[1]!=NULL ? ch[1]->sz : 0)+1));//ans的更新中要有+1,因为这个点自身也参与 //到区间中,同时要*. } }*rootmin,*rootmax; long long jie3[maxnode],jie5[maxnode]; int a[maxnode]; Node *mergemin(Node* &a,Node* &b) { if (a==NULL) return b; if (b==NULL) return a; if (a->r < b->r) { a->ch[1]=mergemin(a->ch[1],b); if (a!=NULL) a->updata(); return a; } else { b->ch[0]=mergemin(a,b->ch[0]); if (b!=NULL) b->updata(); return b; } } Node *mergemax(Node* &a,Node* &b) { if (a==NULL) return b; if (b==NULL) return a; if (a->r > b->r) { a->ch[1]=mergemax(a->ch[1],b); if (a!=NULL) a->updata(); return a; } else { b->ch[0]=mergemax(a,b->ch[0]); if (b!=NULL) b->updata(); return b; } } void split(Node* &o,Node* &a,Node* &b,int x) { if (o==NULL) { a=b=NULL;return; } if (x==0) { a=NULL;b=o;return; } if (x==o->sz) { a=o;b=NULL;return; } if ((o->ch[0]==NULL ? 0 : o->ch[0]->sz)>=x) { split(o->ch[0],a,b,x); o->ch[0]=b;o->updata();b=o; } else { split(o->ch[1],a,b,x-(o->ch[0]==NULL ? 0 : o->ch[0]->sz)-1); o->ch[1]=a;o->updata();a=o; } } void build() { int i,j; Node *q; rootmin=new Node(1,a[1]); for (i=2;i<=100000;++i) { q=new Node(i,a[i]); rootmin=mergemin(rootmin,q); } rootmax=new Node(1,a[1]); for (i=2;i<=100000;++i) { q=new Node(i,a[i]); rootmax=mergemax(rootmax,q); } } long long work(int l1,int r1,int l2,int r2) { Node *p,*q,*x,*y,*z,*k,*i,*j; long long ans=0; if (r1<l2) { split(rootmax,p,q,l1-1); split(q,x,y,r2-l1+1); ans+=x->ans; split(x,z,k,l2-l1); ans-=z->ans;x=mergemax(z,k); split(x,z,k,r1-l1+1); ans-=k->ans;split(k,i,j,l2-1-r1); ans+=(i!=NULL ? i->ans : 0);k=mergemax(i,j); x=mergemax(z,k);q=mergemax(x,y);rootmax=mergemax(p,q); split(rootmin,p,q,l1-1); split(q,x,y,r2-l1+1); ans-=x->ans; split(x,z,k,l2-l1); ans+=z->ans;x=mergemin(z,k); split(x,z,k,r1-l1+1); ans+=k->ans;split(k,i,j,l2-1-r1); ans-=(i!=NULL ? i->ans : 0);k=mergemin(i,j); x=mergemin(z,k);q=mergemin(x,y);rootmin=mergemin(p,q); } if (l2<=r1&&r2>r1) { split(rootmax,p,q,l1-1); split(q,x,y,r2-l1+1); ans+=x->ans; split(x,z,k,l2-l1); ans-=(z!=NULL ? z->ans : 0);x=mergemax(z,k); split(x,z,k,r1-l1+1); ans-=(k!=NULL ? k->ans : 0);x=mergemax(z,k); q=mergemax(x,y);rootmax=mergemax(p,q); split(rootmin,p,q,l1-1); split(q,x,y,r2-l1+1); ans-=x->ans; split(x,z,k,l2-l1); ans+=(z!=NULL ? z->ans : 0);x=mergemin(z,k); split(x,z,k,r1-l1+1); ans+=(k!=NULL ? k->ans : 0);x=mergemin(z,k); q=mergemin(x,y);rootmin=mergemin(p,q); } if (r2<=r1) { split(rootmax,p,q,l1-1); split(q,x,y,r2-l1+1); ans+=(x!=NULL ? x->ans : 0); split(x,z,k,l2-l1); ans-=(z!=NULL ? z->ans : 0);x=mergemax(z,k); q=mergemax(x,y);rootmax=mergemax(p,q); split(rootmin,p,q,l1-1); split(q,x,y,r2-l1+1); ans-=(x!=NULL ? x->ans : 0); split(x,z,k,l2-l1); ans+=(z!=NULL ? z->ans : 0);x=mergemin(z,k); q=mergemin(x,y);rootmin=mergemin(p,q); } return ans; } int main() { freopen("sum.in","r",stdin); freopen("sum.out","w",stdout); int t,l1,l2,r1,r2,i,j; long long ans; jie3[0]=jie5[0]=1; for (i=1;i<=100000;++i) { jie3[i]=(jie3[i-1]*1023)%P; jie5[i]=(jie5[i-1]*1025)%P; a[i]=(int)jie3[i]^(int)jie5[i]; } build(); scanf("%d",&t); for (i=1;i<=t;++i) { scanf("%d%d%d%d",&l1,&r1,&l2,&r2); if (l2<l1) l2=l1; if (r2>=l1) ans=work(l1,r1,l2,r2); else ans=0; printf("%I64d\n",ans); } fclose(stdin); fclose(stdout); }
这题还有一种做法:最大值的时候:考虑离线,ask(i,l,r)表示右端点在1~i之间,左端点在l~r之间的时候的答案,枚举右端点,i-1 -> i的时候,单调栈找到最小的j满足j~i中ai[i]是最大值,对于j~i的位置都标记ai[i]是最大值,并且要加上原来的答案,通过维护区间和v、区间历史和s、区间长度l和一些标记(v'=av+bl,s'=cv+dl+s),就可以算答案了。
bzoj1861 书架
题目大意:一个1~n的序列,取出编号x的数,放到最上最下面、原位置及上下,询问x在第几位和第x位的编号。
思路:fhqtreep,记录每个位置的指针,查询x这个数的排名的时候,从指针往上走。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #define N 80005 using namespace std; struct node{ node *ch[2],*fa; int r,sz,v; node(int v):v(v) {r=rand();sz=1;ch[0]=ch[1]=fa=NULL;} int isd(){return (this == fa->ch[0]);} void updata(){ sz=1; if (ch[0]!=NULL){sz+=ch[0]->sz;ch[0]->fa=this;} if (ch[1]!=NULL){sz+=ch[1]->sz;ch[1]->fa=this;} } }*po[N],*rt=NULL; char ss[N]; node *merge(node *a,node *b){ if (a==NULL) return b; if (b==NULL) return a; if (a->r < b->r){ a->ch[1]=merge(a->ch[1],b); if (a!=NULL) a->updata(); return a; }else{ b->ch[0]=merge(a,b->ch[0]); if (b!=NULL) b->updata(); return b; } } void split(node *o,node* &a,node* &b,int x){ if (o==NULL){a=b=NULL;return;} if (x==0){a=NULL;b=o;return;} if (x== o->sz){a=o;b=NULL;return;} int ls=(o->ch[0]==NULL ? 0 : o->ch[0]->sz); if (ls>=x){ split(o->ch[0],a,b,x); o->ch[0]=b;o->updata();b=o; }else{ split(o->ch[1],a,b,x-ls-1); o->ch[1]=a;o->updata();a=o; } } int query(node *o,int x){ int ls=(o->ch[0]==NULL ? 0 : o->ch[0]->sz); if (ls+1==x) return o->v; if (ls>=x) return query(o->ch[0],x); else return query(o->ch[1],x-ls-1); } int rank(node *o){ int rk=(o->ch[0]==NULL ? 0 : o->ch[0]->sz)+1; while(o->fa != NULL){ if (!o->isd()) rk+=(o->fa->ch[0]==NULL ? 0 : o->fa->ch[0]->sz)+1; o=o->fa; }return rk; } int main(){ int n,m,i,j,x,y,rk;node *q,*p,*w,*z,*e,*f; scanf("%d%d",&n,&m); for (i=1;i<=n;++i){ scanf("%d",&j);q=new node(j); po[j]=q;rt=merge(rt,q); }for (i=1;i<=m;++i){ scanf("%s%d",ss,&x); if (ss[0]=='Q'){printf("%d\n",query(rt,x));continue;} rk=rank(po[x]); if (ss[0]=='A'){printf("%d\n",rk-1);continue;} split(rt,p,q,rk-1);split(q,w,z,1); if (ss[0]=='T'){q=merge(p,z);rt=merge(w,q);} if (ss[0]=='B'){q=merge(p,z);rt=merge(q,w);} if (ss[0]=='I'){ scanf("%d",&y); if (y==0){q=merge(w,z);rt=merge(p,q);} if (y==1){ split(z,e,f,1);z=merge(p,e); q=merge(z,w);rt=merge(q,f); }if (y==-1){ split(p,e,f,p->sz - 1);p=merge(e,w); q=merge(p,f);rt=merge(q,z); } } } }
bzoj3786 星系探索
题目大意:给定一棵树,支持三种操作:(1)查询u到根的权值和;(2)给u的子树的点都加一个值;(3)把u的父亲变为v。
思路:用fhq treap维护dfs序。
注意:如果用treap记录fa,要在split之后把根的fa赋成NULL,否则可能出现fa混乱的情况。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define LL long long using namespace std; int point[N]={0},next[N],n,m; LL ai[N]; char in(){ char ch=getchar(); while(ch<'A'||ch>'Z') ch=getchar(); return ch;} void add(int u,int v){next[v]=point[u];point[u]=v;} struct node{ node *ch[2],*fa; LL sz,del,sm,v,f;int si,r; node(LL x,LL ff){r=rand();f=sz=ff;si=1;del=0LL;sm=v=x;ch[0]=ch[1]=fa=NULL;} void pushdown(){ if (ch[0]!=NULL){ ch[0]->del+=del;ch[0]->v+=ch[0]->f*del; ch[0]->sm+=ch[0]->sz*del; ch[0]->fa=this; }if (ch[1]!=NULL){ ch[1]->del+=del;ch[1]->v+=ch[1]->f*del; ch[1]->sm+=ch[1]->sz*del; ch[1]->fa=this; }del=0LL;} void updata(){ sz=f;sm=v;si=1; if (ch[0]!=NULL){ sz+=ch[0]->sz;si+=ch[0]->si; sm+=ch[0]->sm;ch[0]->fa=this; }if (ch[1]!=NULL){ sz+=ch[1]->sz;si+=ch[1]->si; sm+=ch[1]->sm;ch[1]->fa=this; } } }*rt,*po[N][2],*zh[N]; node *merge(node *a,node *b){ if (b==NULL) return a; if (a==NULL) return b; if (a->r < b->r){ if (a!=NULL) a->pushdown(); a->ch[1]=merge(a->ch[1],b); if (a!=NULL) a->updata(); return a; }else{ if (b!=NULL) b->pushdown(); b->ch[0]=merge(a,b->ch[0]); if (b!=NULL) b->updata(); return b; } } void split(node* &o,node* &a,node* &b,int x){ if (o==NULL){a=b=NULL;return;} if (!x){a=NULL;b=o;return;} if (x == o->si){a=o;b=NULL;return;} o->pushdown(); int ls=(o->ch[0]==NULL ? 0 : o->ch[0]->si); if (ls>=x){ split(o->ch[0],a,b,x); o->ch[0]=b;o->updata();b=o; }else{ split(o->ch[1],a,b,x-ls-1); o->ch[1]=a;o->updata();a=o; } } void dfs(int u){ int i;node *o,*oo; o=new node(ai[u],1LL); po[u][0]=o;rt=merge(rt,o); for (i=point[u];i;i=next[i]) dfs(i); oo=new node(-ai[u],-1LL); po[u][1]=oo;rt=merge(rt,oo);} int getrk(node *o){ int si=(o->ch[0]==NULL ? 0 : o->ch[0]->si)+1; while(o->fa != NULL){ if (o == o->fa->ch[1]) si+=(o->fa->ch[0]==NULL ? 0 : o->fa->ch[0]->si)+1; o=o->fa; }return si;} LL getq(int u){ node *x,*y;int uk;LL sm; uk=getrk(po[u][0]); split(rt,x,y,uk); if (x!=NULL) x->fa=NULL; if (y!=NULL) y->fa=NULL; sm=x->sm; rt=merge(x,y); return sm;} void getc(int u,int v){ int ulk,urk,vr; node *x,*y,*z,*q; ulk=getrk(po[u][0]); urk=getrk(po[u][1]); split(rt,x,y,ulk-1); if (x!=NULL) x->fa=NULL; if (y!=NULL) y->fa=NULL; split(y,z,q,urk-ulk+1); if (z!=NULL) z->fa=NULL; if (q!=NULL) q->fa=NULL; rt=merge(x,q);//printf("%d %d\n",rt->si,rt->sz); vr=getrk(po[v][0]); split(rt,x,y,vr); if (x!=NULL) x->fa=NULL; if (y!=NULL) y->fa=NULL; x=merge(x,z); rt=merge(x,y);} void getf(int u,LL v){ int ulk,urk;node *x,*y,*z,*q; ulk=getrk(po[u][0]); urk=getrk(po[u][1]); split(rt,x,y,ulk-1); if (x!=NULL) x->fa=NULL; if (y!=NULL) y->fa=NULL; split(y,z,q,urk-ulk+1); if (q!=NULL) q->fa=NULL; if (z!=NULL){ z->fa=NULL; z->v+=z->f * v; z->del+=v; z->sm+=z->sz * v; }y=merge(z,q); rt=merge(x,y);} int main(){ int i,u,v;char ch;LL x; scanf("%d",&n);rt=NULL; for (i=2;i<=n;++i){scanf("%d",&u);add(u,i);} for (i=1;i<=n;++i) scanf("%I64d",&ai[i]); dfs(1);scanf("%d",&m); while(m--){ ch=in(); if (ch=='Q'){ scanf("%d",&u); printf("%I64d\n",getq(u)); }if (ch=='C'){ scanf("%d%d",&u,&v); getc(u,v); }if (ch=='F'){ scanf("%d%I64d",&u,&x); getf(u,x); } } }
bzoj2329 括号修复
题目大意:维护一个括号序列。支持操作:(1)区间覆盖;(2)区间翻转(左括号仍然是左括号);(3)区间反转(左括号变为右括号);(4)查询一段区间至少改几个括号使匹配(保证查询区间长度是偶数)。
思路:用fhp treap维护序列,记录这个点的颜色,区间当前和反转后的左右没匹配的括号数,区间大小,三种标记:覆盖、翻转、反转。翻转的操作比较难实现,因为交换左右孩子以及左右没匹配的括号数相当于翻转+反转,所以可以对于翻转操作做一次翻转+反转+反转的操作来实现。
注意:三个标记之间会互相影响,更新顺序和取值的更改要注意。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #define N 100005 using namespace std; char ss[N]; char in(){ char ch=getchar(); while(ch!='('&&ch!=')') ch=getchar(); return ch;} struct node{ node* ch[2]; int r,qr,qs,qi,sz,v,a[2],b[2]; node(char vv){ v=(vv==')');r=rand(); qr=-1;qs=qi=0;sz=1;ch[0]=ch[1]=NULL; if (v){a[0]=b[1]=1;a[1]=b[0]=0;} else{a[0]=b[1]=0;a[1]=b[0]=1;} } void updata(){ int i,vv,la,lb,ra,rb;sz=1; if (ch[0]!=NULL) sz+=ch[0]->sz; if (ch[1]!=NULL) sz+=ch[1]->sz; for (i=0;i<=1;++i){ vv=v^i; if (ch[0]!=NULL){la=ch[0]->a[i];lb=ch[0]->b[i];} else la=lb=0; if (ch[1]!=NULL){ra=ch[1]->a[i];rb=ch[1]->b[i];} else ra=rb=0; if (vv){ if (lb>=1+ra){a[i]=la;b[i]=lb-1-ra+rb;} else{a[i]=la+1+ra-lb;b[i]=rb;} }else{ if (lb+1>=ra){a[i]=la;b[i]=lb+1-ra+rb;} else{a[i]=la+ra-1-lb;b[i]=rb;} } } } void pushdown(){ for (int i=0;i<=1;++i) if (ch[i]!=NULL){ if (qr>=0){ ch[i]->qr=qr;ch[i]->v=qr; ch[i]->qi=ch[i]->qs=0; if (qr){ ch[i]->a[0]=ch[i]->b[1]=ch[i]->sz; ch[i]->a[1]=ch[i]->b[0]=0; }else{ ch[i]->a[0]=ch[i]->b[1]=0; ch[i]->a[1]=ch[i]->b[0]=ch[i]->sz; } }if (qs){ ch[i]->qs ^=1;ch[i]->v ^=1; swap(ch[i]->ch[0],ch[i]->ch[1]); swap(ch[i]->a[0],ch[i]->b[0]); swap(ch[i]->a[1],ch[i]->b[1]); }if (qi){ ch[i]->qi ^=1;ch[i]->v ^=1; swap(ch[i]->a[0],ch[i]->a[1]); swap(ch[i]->b[0],ch[i]->b[1]); } } qr=-1;qs=qi=0; } }*rt=NULL; node *merge(node *a,node *b){ if (b==NULL) return a; if (a==NULL) return b; if (a->r < b->r){ if (a!=NULL) a->pushdown(); a->ch[1]=merge(a->ch[1],b); if (a!=NULL) a->updata(); return a; }else{ if (b!=NULL) b->pushdown(); b->ch[0]=merge(a,b->ch[0]); if (b!=NULL) b->updata(); return b; } } void split(node* &o,node* &a,node* &b,int x){ if (o==NULL){a=b=NULL;return;} if (!x){a=NULL;b=o;return;} if (x==o->sz){a=o;b=NULL;return;} int ls=(o->ch[0]==NULL ? 0 : o->ch[0]->sz); o->pushdown(); if (ls>=x){ split(o->ch[0],a,b,x); o->ch[0]=b;o->updata();b=o; }else{ split(o->ch[1],a,b,x-ls-1); o->ch[1]=a;o->updata();a=o; } } void replace(int l,int r,int k){ node *x,*y,*p,*q; split(rt,x,y,l-1); split(y,p,q,r-l+1); p->qi=p->qs=0; p->v=p->qr=k; if (k){ p->a[0]=p->b[1]=p->sz; p->a[1]=p->b[0]=0; }else{ p->a[0]=p->b[1]=0; p->a[1]=p->b[0]=p->sz; }y=merge(p,q); rt=merge(x,y); } void swa(int l,int r){ node *x,*y,*p,*q; split(rt,x,y,l-1); split(y,p,q,r-l+1); p->qs ^=1; swap(p->ch[0],p->ch[1]); swap(p->a[0],p->b[0]); swap(p->a[1],p->b[1]); p->qi ^=1; swap(p->a[0],p->a[1]); swap(p->b[0],p->b[1]); y=merge(p,q); rt=merge(x,y); } void invert(int l,int r){ node *x,*y,*p,*q; split(rt,x,y,l-1); split(y,p,q,r-l+1); p->qi ^=1;p->v ^=1; swap(p->a[0],p->a[1]); swap(p->b[0],p->b[1]); y=merge(p,q); rt=merge(x,y); } int query(int l,int r){ node *x,*y,*p,*q;int ans,xa,xb; split(rt,x,y,l-1); split(y,p,q,r-l+1); xa=p->a[0];xb=p->b[0]; ans=(xa+1)/2+(xb+1)/2; y=merge(p,q); rt=merge(x,y); return ans; } int main(){ int n,m,i,j,l,r;char ch; scanf("%d%d%s",&n,&m,ss); for (i=0;i<n;++i){ node *o;o=new node(ss[i]); rt=merge(rt,o); }while(m--){ scanf("%s%d%d",ss,&l,&r); if (ss[0]=='R'){ch=in();replace(l,r,ch==')');} if (ss[0]=='S') swa(l,r); if (ss[0]=='I') invert(l,r); if (ss[0]=='Q') printf("%d\n",query(l,r)); } }