大大大大大!!!!!!数据结构题。
太大了:
法一:线段树动态开点:注意n有1e8于是考虑m影响的。
法二:两个SPLAY:目的是在n有1e8时一棵维护名次,一棵维护编号。目的是解决n
原理:修改k:则剖成(1,k-1)(k,k)(k+1,n);
最多复杂度:logn*m;
法三::一个SPLAY,另外一个用map就可以了。用一个in记录是否到了前端与后缀。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<map> using namespace std; inline void read(int &x){//fast read int f=1; x=0; 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(); } x*=f; } int n,m,d;//d is the max num that the tree could expand using by the SET struct SET{//a set to make sure the line is priority #define N (int) 2e5 int a[N*3]; int id[N];//idx just record the left sum and that is priority int n; int insert(int idx){//维持单调的本质原因是为了保证在查询编号时可以得到在哪一块 n++; id[n]=idx; int i=n+d;//we have two sum should be recorded; a[i]=1; while(i>>=1){ a[i]++; } return n; } void del(int i){// del a point and expand 1 to 3 points i+=d; a[i]=0; while(i>>=1){ a[i]--; } } int rank(int i){//give you a sum and que the rank; i+=d; int ans=a[i]; while(i){ if(i%2==1){ ans+=a[i-1]; } i/=2; } return ans; } int get_by_rank(int x){//give you the rank ans que the sum; int i=1; while(i<=d){ if(x<=a[i*2]){ i*=2; } else{ x-=a[i*2]; i=i*2+1; } } return id[i-d]; } }pre,suf;//pre尚未开封的左端(名次小)suf尚未开封的右端(名次大) struct SPlAY{ #define cl(x) c[x][0] #define cr(x) c[x][1] int fa[N]; int c[N][2]; int siz[N]; int v[N]; int rt; int n; int rank(int x){ int ans=x; int i=rt; while(i){ if(v[i]>x){ i=cl(i); } else{ ans-=siz[cl(i)]+1; i=cr(i); } } return ans; } void sc(int y,int x,bool flag){ fa[x]=y; c[y][flag]=x; } bool get(int x){ return x==c[fa[x]][1]; } void pushup(int x){ siz[x]=siz[cl(x)]+siz[cr(x)]+1; } void rotate(int x){ int y=fa[x]; bool flag=get(x); if(y==rt){ fa[rt=x]=0; } else{ sc(fa[y],x,get(y)); } sc(y,c[x][!flag],flag); sc(x,y,!flag); pushup(y); } void splay(int x,int to=0/*depends on the root*/){ int y; while(y=fa[x],y!=to){ if(fa[y]==to){ rotate(x); break; } else{ rotate(get(x)==get(y)?y:x); rotate(x); } } pushup(x); } void insert(int x){//insert the sum to the SPLAY tree n++; v[n]=x; siz[n]=1; if(!rt){ rt=n; return; } int i=rt; while(1){ bool d=v[i]<x; if(c[i][d]) i=c[i][d]; else{ sc(i,n,d); splay(n); return; } } } map<int,int> dy; int DY(int x){//hash the changing name of the num if(!dy.count(x)){ return x; } return dy[x]; } int get_by_rank(int x){//got the sum get the rank值找编号 if(!rt){ return DY(x); } int i=rt; int i0; int rank=1; while(i){ i0=i; if(x>v[i]-(siz[cl(i)]+rank)){ rank+=siz[cl(i)]+1; i=c[i][1]; } else{ i=c[i][0]; } } splay(i0); return DY(x+rank-1); } }out; const int PRE=1;//PRE means at the head of the queue const int SUF=2;//SUF means at the tail of the queue struct point{ int in,id;//in means if that has been in the queue already and the location that is }; map<int,point> belong;//the location of the i; point &B(int x){//这个函数的目的实在映射中寻找这个编号的位置.打传递是因为要通过这个改变belong所hash的值 if(!belong.count(x)) belong[x]=(point){0,x};//如果没有hash过那么必然这时还没有过挪动到队首或队尾 return belong[x]; } int get_rank(int x){//已知编号求名次 if(B(x).in==1){ return pre.a[1]+1-pre.rank(B(x).id);//a 数组在这里表示最左端名次(最大),然后加一是因为计算自己。 } if(B(x).in==2){ return n-suf.a[1]+suf.rank(B(x).id); } return pre.a[1]+out.rank(B(x).id);//如果已经加入splay内,则先加上右端所有的值然后再加上splay中的rank } //引理:pre中的最大名次<out中的最小名次<=out中的最大名次<suf中的最小名次。 int get_by_rank(int x){//已知名次求编号。 if(x<=pre.a[1]){//引理1 return pre.get_by_rank(pre.a[1]-x+1);//在pre中的名次是总名次-实际名次+1,因为pre是倒过来的 } if(x>n-suf.a[1]){//引理3 return suf.get_by_rank(x-(n-suf.a[1]));//减去suf之前的所有名次,则是在suf中的实际名次。 }//引理2 return out.get_by_rank(x-pre.a[1]);//减去pre中的所有名次则是在out中的名次。 } int main(){ read(n); read(m); // cout<<n<<" "<<m<<endl; for(d=1;d<m;d*=2);// get the max sum may be d--; int lastans=0;//must online for(int i=1;i<=m;i++){ // cout<<"number "<<i<<endl; int flag; read(flag); int ans; if(flag==4){ int k; read(k); k-=lastans; ans=get_by_rank(k);//已知名次反向求编号。 } else{ int k; read(k); k-=lastans; ans=get_rank(k);//这三中操作都是要求用编号求名次。 if(flag==1){ int to; read(to); to-=lastans; B(to)=B(k); if(B(k).in){//出现 in 则必然是改变块,否则是原块 (B(k).in==PRE?pre:suf).id[B(k).id]=to;//change the sum to the loc should be and hash the change sum } else{ out.dy[B(k).id]=to;//if not hash the change sum; } } else{//必然会挪动到队首或队尾 if(B(k).in==0){//如果没有挪动过,那么在SPLAY插入这个下标。 out.insert(B(k).id); } else (B(k).in==PRE?pre:suf).del(B(k).id);//因为会被挪动过,所以删去原本的点。 if(flag==2){ B(k)=(point){PRE,pre.insert(k)};//go ahead } else{ B(k)=(point){SUF,suf.insert(k)};//turn back } } } printf("%d\n",ans); lastans=ans;//must online } return 0;//or wrong answer }
注解很丰富。