【NOIP2017】列队 splay
当年太菜了啊,连60分的暴力都没拿满,只打了一个30分的。
考虑到这题最多只会询问到30W个点,且整个矩阵会去到30W×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 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!