洛谷P3960 列队【Splay】

本文介绍了一种使用Splay树数据结构解决军训方阵中学生离队问题的方法。通过建立n+1个Splay树,分别维护每行的前m-1个数和最后一列,实现对学生离队事件的高效处理。文章详细描述了Splay树的操作流程,包括旋转、查找、插入和删除等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

Sylvia 是一个热爱学习的女♂孩子。

前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。

Sylvia 所在的方阵中有n×mn \times mn×m名学生,方阵的行数为 nnn,列数为 mmm

为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 1 到 n×mn \times mn×m 编上了号码(参见后面的样例)。即:初始时,第 iii行第jjj列 的学生的编号是 (i−1)×m+j(i-1) \times m+j(i1)×m+j

然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天 中,一共发生了 $ q$件这样的离队事件。每一次离队事件可以用数对 (x,y)(1≤x≤n,1≤y≤m)(x,y) (1 \le x \le n, 1 \le y \le m)(x,y)(1xn,1ym)描述,表示第 xxx 行第 $y $列的学生离队。

在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:

向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条 指令之后,空位在第 xxx 行第 mmm 列。

向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条 指令之后,空位在第 nnn 行第 mmm列。

教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后, 下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第nnn 行 第 mmm 列一个空位,这时这个学生会自然地填补到这个位置。

因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学 的编号是多少。

注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后 方阵中同学的编号可能是乱序的。

输入格式:

输入共q+1q+1q+1 行。

第 1 行包含 3 个用空格分隔的正整数 n,m,qn, m, qn,m,q,表示方阵大小是 nnnmmm 列,一共发 生了qqq 次事件。

接下来 qqq 行按照事件发生顺序描述了 qqq 件事件。每一行是两个整数 x,yx, yx,y用一个空 格分隔,表示这个离队事件中离队的学生当时排在第 xxx 行第 yyy 列。

输出格式:

按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学 生的编号。
在这里插入图片描述
数据保证每一个事件满足 1≤x≤n,1≤y≤m1 \le x \le n,1 \le y \le m1xn,1ym


题目分析

我们建立n+1个Splay
nnn个用来维护每行的前m−1m-1m1个数
还有一个用来维护最后一列

对于一个询问(x,y)(x,y)(x,y)
维护最后一列的Splay找到第xxx个数并弹出
将找到的数插入第xxx行Splay的末尾
xxx行的Splay找到第yyy个数并弹出
将这个数插入最后一列Splay的末尾

由于数据范围有3e5,所以还要想办法优化空间
Splay每个结点维护一个连续区间[ll,rr][ll,rr][ll,rr]的数
若要弹出的数xxx在某个结点内部
就把这个节点分裂成三个[ll,x−1],[x,x],[x+1,rr][ll,x-1],[x,x],[x+1,rr][ll,x1],[x,x],[x+1,rr]

可能本人Splay太丑 (或者是我太丑),吸了氧还是一直卡80 QAQ
哪天回头再大力卡一下常试试


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long lt;
#define RI register int
#define li inline
#define pir pair<lt,lt>
#define update(x) size[x]=size[ch[x][0]]+size[ch[x][1]]+R[x]-L[x]+1

li lt read()
{
    lt x=0,f=1;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

void print(lt x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

const int maxn=3000010;
lt n,m,q,cnt;
lt L[maxn],R[maxn],size[maxn];
int rt[maxn],fa[maxn],ch[maxn][2];

li void rotate(int &p,int x)
{
    int y=fa[x],z=fa[y];
    int d=(ch[y][0]==x);
    if(y==p) p=x;
    else if(ch[z][0]==y) ch[z][0]=x;
    else ch[z][1]=x;
    fa[y]=x; fa[ch[x][d]]=y; fa[x]=z;
    ch[y][d^1]=ch[x][d]; ch[x][d]=y;
    update(y); update(x);
}

li void splay(int& p,int x)
{   
    while(x!=p) 
    {
        int y=fa[x],z=fa[y];
        if(y!=p) 
        {
            if((ch[y][0]==x)^(ch[z][0]==y)) rotate(p,x);
            else rotate(p,y);
        }
        rotate(p,x);
    }
}

void ins(int &p,int pa,lt num) 
{
    if(!p){ p=++cnt;L[p]=R[p]=num;fa[p]=pa;size[p]=1; return;}
    ins(ch[p][1],p,num); update(p);
}

pir find(int p,lt k)
{
    lt ss=size[ch[p][0]];
    if(k<=ss) return find(ch[p][0],k);
    else if(k<=ss+R[p]-L[p]+1) return make_pair(p,L[p]+k-1-ss);
    else return find(ch[p][1],k-ss-(R[p]-L[p]+1)); 
}

li void del(int &p,int x)
{
    splay(p,x);
    if(ch[p][0]*ch[p][1]==0) p=ch[p][0]+ch[p][1];
    else
    {
        int y=ch[p][1];
        while(ch[y][0]) y=ch[y][0];
        splay(ch[p][1],y);
        fa[ch[p][0]]=y; ch[y][0]=ch[p][0];
        update(y); update(p);
        p=ch[p][1];
    }
    ch[fa[p]][0]=ch[fa[p]][1]=fa[p]=0; 
}

li void split(int &p,int x,lt num)
{
    splay(p,x);
    if(L[p]==R[p]) del(p,x);
    else if(L[p]==num) ++L[p],update(p);
    else if(R[p]==num) --R[p],update(p);
    else 
    {
        L[++cnt]=num+1; R[cnt]=R[p]; 
        size[cnt]=R[cnt]-L[cnt]+1; 
        R[p]=num-1;
        
        int y=ch[p][1];
        if(y){
            while(ch[y][0]) y=ch[y][0];
            splay(ch[p][1],y);
            fa[cnt]=y,ch[y][0]=cnt;
            update(y);
        }
        else ch[p][1]=cnt,fa[cnt]=p;
        update(p);
    }
}

int main()
{
    n=read();m=read();q=read();
    
    for(RI i=1;i<=n;++i)
    rt[i]=++cnt,L[cnt]=(i-1)*m+1,R[cnt]=i*m-1,size[cnt]=m-1;
    for(RI i=1;i<=n;++i) ins(rt[0],0,i*m),splay(rt[0],cnt);
    
    while(q--)
    {
        int x=read(),y=read();
        pir tt1=find(rt[0],x),tt2;
        del(rt[0],tt1.first);
        if(y!=m)
        {
            tt2=find(rt[x],y); split(rt[x],tt2.first,tt2.second);
            ins(rt[x],0,tt1.second);
        }
        else tt2=tt1;
        ins(rt[0],0,tt2.second); 
        print(tt2.second); putchar('\n');
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值