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