洛谷P3960 列队【Splay】

题目描述

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

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

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

为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 1 到 n × m n \times m n×m 编上了号码(参见后面的样例)。即:初始时,第 i i i行第 j j j列 的学生的编号是 ( 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)描述,表示第 x x x 行第 $y $列的学生离队。

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

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

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

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

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

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

输入格式:

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

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

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

输出格式:

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


题目分析

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
校园悬赏任务平台对字典管理、论坛管理、任务资讯任务资讯公告管理、接取用户管理、任务管理、任务咨询管理、任务收藏管理、任务评价管理、任务订单管理、发布用户管理、管理员管理等进行集中化处理。经过前面自己查阅的网络知识,加上自己在学校课堂上学习的知识,决定开发系统选择小程序模式这种高效率的模式完成系统功能开发。这种模式让操作员基于浏览器的方式进行网站访问,采用的主流的Java语言这种面向对象的语言进行校园悬赏任务平台程序的开发,在数据库的选择上面,选择功能强大的Mysql数据库进行数据的存放操作。校园悬赏任务平台的开发让用户查看任务信息变得容易,让管理员高效管理任务信息。 校园悬赏任务平台具有管理员角色,用户角色,这几个操作权限。 校园悬赏任务平台针对管理员设置的功能有:添加并管理各种类型信息,管理用户账户信息,管理任务信息,管理任务资讯公告信息等内容。 校园悬赏任务平台针对用户设置的功能有:查看并修改个人信息,查看任务信息,查看任务资讯公告信息等内容。 系统登录功能是程序必不可少的功能,在登录页面必填的数据有两项,一项就是账号,另一项数据就是密码,当管理员正确填写并提交这二者数据之后,管理员就可以进入系统后台功能操作区。项目管理页面提供的功能操作有:查看任务,删除任务操作,新增任务操作,修改任务操作。任务资讯公告信息管理页面提供的功能操作有:新增任务资讯公告,修改任务资讯公告,删除任务资讯公告操作。任务资讯公告类型管理页面显示所有任务资讯公告类型,在此页面既可以让管理员添加新的任务资讯公告信息类型,也能对已有的任务资讯公告类型信息执行编辑更新,失效的任务资讯公告类型信息也能让管理员快速删除。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值