NOIP2017 列队

NOIP2017 列队

题目大意

有一个\(n*m\)的列队方格。
一开始,这个列队中\((i,j)\)的人的编号为\((i-1)m+j\)。
每次下达一个指令\((x,y)\),使得\((x,y)\)这个位置的人出列。
然后:

  • 向左看齐,所有人向左填补空位。
  • 向前看齐,所有人向前填补空位。

不难发现两次看齐后空位变到了\((n,m)\),然后令刚才出列的人回到这个位置。
对于依次进行的\(Q\)次指令,你需要输出出列的人的编号。
数据范围:\(n,m,Q \leq 3*10^5\)

前言

NOIP此题50分选手现在依旧觉得:好神啊这题(QwQ)。
我自己的想法是:
对于每一行和最后一列维护一个\(Splay\),把连续的整块看做一个点。
每次先二分找到对应的整块,然后把整块分开再重新插入\(Splay\),一直做下去即可。
超级暴力的\(Splay\)直接硬上......
我发现自己写不出来,瑟瑟发抖......(后来发现ZSY和YYB写的就是这个,太强了ORZ!)
所以比较好写的做法是:

题解

依旧顺着上面说的那种做法,
修改\((x,y)\)影响的只有第\(x\)行和第\(m\)列。
那么我们开\(n\)棵 线段木(动态开点) 来维护每行的前\(m-1\)列,再开一棵维护最后一列。
可以发现初始每一棵线段木中存的元素是连续的一段。
所以我们用线段木维护初始每一行的元素,每次二分查找看答案是不是初始元素。
如果是初始元素那么直接输出就行了。
如果不是呢?
我们开\(vector\)记录每一行和最后一列中后来加入的元素
如果不是直接到\(vector\)中取即可。
这题就做完了,线段木写起来真的非常的舒服(QwQ)。

实现代码

#include<bits/stdc++.h>
#define RG register
#define IL inline
#define _ 300005
using namespace std;

IL int gi(){
    RG int data = 0 , m = 1; RG char ch = 0;
    while(ch != '-' && (ch<'0' || ch > '9')) ch = getchar();
    if(ch == '-'){m = 0; ch = getchar();}
    while(ch>='0' && ch<='9'){data = (data<<1) + (data<<3) + ch - '0' ;  ch = getchar();}
    return (m) ? data : -data ;
}

struct Segment_Tree{int ls,rs,sz;}t[60*_];
int n,m,f[_],rt[_],tot,Lg,Q; vector<long long>vec[_] ; 

int Query(RG int o,RG int l,RG int r,RG int K){
    if(l == r) return l ;
    RG int mid = (l + r) >> 1 , SZ = (mid-l+1) - t[t[o].ls].sz ;
    if(K <= SZ) return Query(t[o].ls , l , mid , K) ;
    else if(K > SZ) return Query(t[o].rs , mid + 1 , r , K - SZ) ;
}
void Delete(RG int &o,RG int l,RG int r,RG int p){
    if(!o)o = ++tot ;
    ++ t[o].sz; if(l == r)return ;
    RG int mid = (l + r) >> 1;
    if(p <= mid) Delete(t[o].ls , l , mid , p) ;
    else if(p > mid) Delete(t[o].rs , mid + 1 , r , p) ;
}

IL long long Delete_line(RG int x,RG int kth){
    RG int id = Query(rt[x] , 1 , Lg , kth) ;
    Delete(rt[x] , 1 , Lg , id) ;
    return (id <= m-1) ? 1ll * (x-1) * m + id : vec[x][id-m];
}
IL long long Delete_row(RG int kth){
    RG int id = Query(rt[n+1] , 1 , Lg , kth) ;
    Delete(rt[n+1] , 1 , Lg , id) ;
    return (id <= n) ? 1ll * id * m : vec[n+1][id-n-1];
}

int main(){
    n = gi(); m = gi(); Q = gi();
    Lg = max(n , m) + Q + 1;
    long long id , id2 ;
    while(Q --){
        RG int x = gi() , y = gi() ;
        if(y ^ m){
            id = Delete_line(x , y) ;  id2 = Delete_row(x) ;
            vec[x].push_back(id2) ;   vec[n+1].push_back(id) ;
        }
        else{
            id = Delete_row(x) ;
            vec[n+1].push_back(id) ;
        }
        printf("%lld\n" , id) ;
    }return 0;
} 

原文地址:https://www.cnblogs.com/GuessYCB/p/8686320.html

时间: 2024-10-31 12:45:46

NOIP2017 列队的相关文章

luoguP3960 [noip2017]列队(树状数组)

www.cnblogs.com/shaokele/ luoguP3960 [noip2017]列队 Time Limit: 2 Sec Memory Limit: 512 MB Description Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有 \(n \times m\) 名学生,方阵的行数为 \(n\) ,列数为 \(m\) . 为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序

NOIP2017列队(phalanx)解题报告

列队作为NOIP2017最后一道题,其实并不难,只是相对于其它题目,有点小小的工业 首先,这道题我用splay维护的,如果你不会splay,又想学一下splay,可以来[这里](http://www.cnblogs.com/dengyixuan/p/7895910.html)学一学,接下来步入正题 首先这道题和往年一样,特殊数据会给你极大的启发,在考试时,看到x=1,只有一行的数据时,我就想到,可以维护一颗平衡数,每次只查询(x,y)由于x=1,所以只要查询队列中第y大即可,那么你再想一想,对于

NOIP2017D2T3 列队—Treap

NOIP2017列队 Description Sylvia 是一个热爱学习的女孩子.  前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia所在的方阵中有n × m名学生,方阵的行数为 n,列数为m.  为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中的学生从 1 到 n × m 编上了号码(参见后面的样例).即:初始时,第 i 行第 j 列的学生的编号是(i−1)×m+j.  然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队.

平衡树讲解(旋转treap,非旋转treap,splay)

在刷了许多道平衡树的题之后,对平衡树有了较为深入的理解,在这里和大家分享一下,希望对大家学习平衡树能有帮助. 平衡树有好多种,比如treap,splay,红黑树,STL中的set.在这里只介绍几种常用的:treap和splay(其中treap包括旋转treap和非旋转treap). 一.treap treap这个词是由tree和heap组合而成,意思是树上的的堆(其实就是字面意思啦qwq).treap可以说是由二叉搜索树(BST)进化而来,二叉搜索树每个点满足它左子树中所有点权值都比它小,它右子

数据结构小节(上)

蒟蒻最近学习了一些数据结构,下面是蒟蒻的总结. $$$$ 1.线段树合并 所谓线段树合并,字面上理解,就是将两颗线段树合并在一起,所以多用于权值 线段树,而且多在 树形结构 的题中出现.然而对两颗满二叉树的合并一次复杂 度会达到\(O(nlog_2n)\) 对于总操作\(m\),一般来说每次就是动态开点复杂度\(O(mlog_2n)\).然后考虑每 次合并的活,我们每一次只会把 有的 都加起来,接着递归向下处理,把 没有 的直接接到新的线段树上.所以复杂度其实是关乎重叠部分的.如果重叠的越 多,

2019年7月博客汇总上

[CQOI2014]排序机械臂 萌新刚学Splay,被这题卡了好久. 写一写自己死去的经过. 死亡x1 没看清题中对于稳定排序的描述而误入歧途,想直接在Splay中查找min,后来发现这种做法是错的. 死亡x2 rotate写错了233 死亡x3 后来改用直接在数组中记录对应的点在Splay中位置的做法,由于懒采用了翻转一次删一个点的做法. 因为想到如果将目标节点Splay到根后它的左儿子都是序列中在它左边的点,所以直接修改了左边. 后来发现这种方法会导致哨兵也被翻转而导致TLE. 死亡x4 由

【NOIP2017】列队(Splay)

[NOIP2017]列队(Splay) 题面 洛谷 题解 其实好简单啊... 对于每一行维护一棵\(Splay\) 对于最后一列维护一棵\(Splay\) \(Splay\)上一个节点表示一段区间 每次出去一个人就是把当前的\(Splay\)的一个节点拆分成\(3\)个 然后就很简单了.. 细节比较多.. #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #incl

[jzoj]5478.【NOIP2017提高组正式赛】列队

Link https://jzoj.net/senior/#main/show/5478 Description Sylvia 是一个热爱学习的女孩子.       前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia所在的方阵中有n × m名学生,方阵的行数为 n,列数为 m.       为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中从 1 到 n × m 编上了号码(参见后面的样例).即:初始时,第 i 行第 j 列的学生的编号是(

NOIP2017 Day2 T3 列队(treap)

可以直接用treap上大模拟...n+1个treap维护n行的前m-1个点和最后一列. 需要支持删除一个点或者一段区间,而空间并不支持存下所有的点的时候,可以用一个点代替一个区间,记录区间首项的值和区间长度,这样每次查询某个点x的时候就可以用x在某个点y代表的区间里的rank来得到x的值,然后把x删去的时候,就把y这个区间从$[l,r]$拆分成$[l,x-1]$和$[x+1,r]$,重新加入. 类似的题有NOI超级钢琴 #include<iostream> #include<cstrin