【NOIP2017】列队 splay
当年太菜了啊,连$60$分的暴力都没拿满,只打了一个$30$分的。
考虑到这题最多只会询问到$30W$个点,且整个矩阵会去到$30W\times 30W$,显然不能将所有的点存下来。
对于每一行(除最右侧的数)我们维护一个$splay$,存储该位置的值,考虑到矩阵很大肯定不能全部开下,我们用一个节点存储一个区间(详情见代码)
这样会发现最右侧一列被吃了,所以我们还要维护一个$splay$去存储这一列的数。
对于一组询问$(x,y)$,当$y=m$时,那么显然直接在最右侧的$slpay$上删去某个位置的值,然后丢到这个$splay$的末尾即可。
当$y≠m$时,我们就在第x课splay中将包含有y这个数的区间拎出来,砍成三段(详情见代码),从这个$splay$中删去这个数字,然后重复$y=m$时所做的操作。
这么做的时间复杂度是$O(m\ log\ n+n\ log\ m+q\ log\ n+q\ log\ m)$,常数有点大但反正过了。
代码貌似不短qwq,可能是我的写法比较垃圾
1 #include<bits/stdc++.h> 2 #define M 500005 3 #define L long long 4 #define lc(x) ch[x][0] 5 #define rc(x) ch[x][1] 6 using namespace std; 7 8 int ch[M*8][2]={0},siz[M*8]={0},fa[M*8]={0},cnt[M*8]={0};L a[M*8]={0}; 9 int rt[M]={0},rty=0,use=0; 10 L n,m,q,numuse; 11 void pushup(int x){siz[x]=siz[lc(x)]+siz[rc(x)]+cnt[x];} 12 void rotate(int x,int &k){ 13 int y=fa[x],z=fa[y]; 14 int l=lc(y)!=x,r=l^1; 15 if(y==k) k=x; 16 else{ 17 if(lc(z)==y) lc(z)=x; 18 else rc(z)=x; 19 } 20 fa[x]=z; fa[y]=x; fa[ch[x][r]]=y; 21 ch[y][l]=ch[x][r]; ch[x][r]=y; 22 pushup(y); pushup(x); 23 } 24 void splay(int x,int &k){ 25 while(x!=k){ 26 int y=fa[x],z=fa[y]; 27 if(y!=k){ 28 if((lc(z)==y)^(lc(y)==x)) rotate(x,k); 29 else rotate(y,k); 30 } 31 rotate(x,k); 32 } 33 } 34 35 int find(int x,int k){ 36 if(!x) return 0; 37 if(k<=siz[lc(x)]) return find(lc(x),k); 38 if(k<=siz[lc(x)]+cnt[x]) return x; 39 return find(rc(x),k-siz[lc(x)]-cnt[x]); 40 } 41 void ins(int &x,int k,int id){ 42 if(!x) {x=id; pushup(id); return;} 43 if(k<=siz[lc(x)]) ins(lc(x),k,id),fa[lc(x)]=x; 44 else ins(rc(x),k-siz[lc(x)]-cnt[x],id),fa[rc(x)]=x; 45 pushup(x); 46 } 47 void merge(int x,int y,int &rt){ 48 //合并两个splay 49 if(!x) {rt=y; return;} 50 if(!y) {rt=x; return;} 51 rt=x; fa[x]=0; 52 while(rc(x)) x=rc(x); 53 rc(x)=y; fa[y]=x; 54 splay(y,rt); 55 } 56 57 int query(int x,int y){ 58 //询问x,y位置的值,并且从splay中分离出来 59 int now=find(rt[x],y); 60 if(!now) return 0; 61 splay(now,rt[x]);//找出点的位置 62 if(cnt[now]==1) return now; 63 int lch=lc(now),rch=rc(now); 64 int cntl=y-siz[lc(now)]; 65 if(cntl-1){ 66 use++; 67 a[use]=a[now]; 68 cnt[use]=cntl-1; 69 lc(use)=lc(now); fa[lc(use)]=use; 70 lc(now)=use; fa[use]=now; 71 pushup(use); 72 } 73 a[now]+=cntl-1; cnt[now]-=cntl-1; 74 if(cnt[now]>1){ 75 use++; 76 a[use]=a[now]+1; 77 cnt[use]=cnt[now]-1; 78 rc(use)=rc(now); fa[rc(use)]=use; 79 rc(now)=use; fa[use]=now; 80 pushup(use); 81 } 82 cnt[now]=1; 83 pushup(now); 84 return now; 85 } 86 87 int main(){ 88 // freopen("in.txt","r",stdin); 89 // freopen("out.txt","w",stdout); 90 cin>>n>>m>>q; 91 numuse=n*m; 92 for(L i=1;i<=n;i++){ 93 rt[i]=++use; 94 a[use]=(i-1)*m+1; 95 cnt[use]=siz[use]=m-1; 96 a[++use]=i*m; cnt[use]=1; 97 ins(rty,i-1,use); 98 splay(use,rty); 99 } 100 while(q--){ 101 int x,y; scanf("%d%d",&x,&y); 102 int now=query(x,y),outy=0;L outnum; 103 if(now){ 104 outnum=a[now]; 105 merge(lc(now),rc(now),rt[x]); 106 }else outy=1; 107 now=find(rty,x); 108 splay(now,rty); 109 if(outy) outnum=a[now]; 110 merge(lc(now),rc(now),rty); 111 lc(now)=rc(now)=0; 112 if(outy==0){ 113 ins(rt[x],m-1,now); 114 splay(now,rt[x]); 115 } 116 a[++use]=outnum; cnt[use]=1; 117 ins(rty,n-1,use); splay(use,rty); 118 printf("%lld\n",outnum); 119 } 120 }