【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 }

 

posted @ 2018-09-27 08:54  AlphaInf  阅读(285)  评论(0编辑  收藏  举报