[NOIp 2017]列队
Description
Sylvia 是一个热爱学习的女♂孩子。
前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。
Sylvia 所在的方阵中有n×m名学生,方阵的行数为 n,列数为 m。
为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 1 到 n×m 编上了号码(参见后面的样例)。即:初始时,第 iii 行第 jjj 列 的学生的编号是(i−1)×m+j。
然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天 中,一共发生了 q 件这样的离队事件。每一次离队事件可以用数对(x,y)(1≤x≤n,1≤y≤m)描述,表示第 x 行第 y 列的学生离队。
在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:
向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条 指令之后,空位在第 x 行第 m 列。
向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条 指令之后,空位在第 n 行第 m 列。
教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后, 下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 n 行 第 m 列一个空位,这时这个学生会自然地填补到这个位置。
因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学 的编号是多少。
注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后 方阵中同学的编号可能是乱序的。
正解:线段树/树状数组/平衡树
转载自http://www.cnblogs.com/Yuzao/p/7920813.html#3867765
\(30\%\):开个 \(n*m\) 的数组模拟即可
\(50\%\):发现只有500行有改动,所以单独拿出这500行和最后一列,模拟即可.
\(80\%\):只有一行的话,我们就开一个 \(m+q\) 的数组,然后树状数组维护每一个位置是否有人,并且维护每一个位置的人的id,这样就会产生很多空位,询问就是查找第 \(y\) 个有人的位置的id,二分+树状数组 或 直接线段树查找第k大即可,与 \(70\) 分不同的是,还需要再维护最后一列,像行一样维护即可
\(100\%\):和 \(80\) 分类似,想到有很多位置根本没有大的变动,我们像之前一样,我们把只需要出队的位置删除即可,所以我们维护每一个位置是否被删,但是不太好存,所以用动态开点线段树标记删除位置,然后像之前一样二分找出第 \(y\) 个有人的位置即可,还有一个不同的是,id数组需要动态维护,所以开个vector存即可,所以100和80的区别仅在于是否使用动态内存.
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 typedef long long lol; 8 const int N=300000; 9 int chl[10000005],chr[10000000+5],cnt[10000000+5],root[N+5],tot; 10 vector<lol>G[N+5]; 11 lol n,m,sum,q; 12 lol query(int rt,int l,int r,lol k) 13 { 14 if (l==r) return l; 15 int mid=(l+r)/2; 16 if (mid-l+1-cnt[chl[rt]]>=k) return query(chl[rt],l,mid,k); 17 else return query(chr[rt],mid+1,r,k-(mid-l+1-cnt[chl[rt]])); 18 } 19 void del(int &rt,int l,int r,lol k) 20 { 21 if (!rt) rt=++tot; 22 cnt[rt]++; 23 if (l==r) return; 24 int mid=(l+r)/2; 25 if (k<=mid) del(chl[rt],l,mid,k); 26 else del(chr[rt],mid+1,r,k); 27 } 28 lol zyys1(lol x,lol kind) 29 { 30 lol pos=query(root[n+1],1,sum,x); 31 del(root[n+1],1,sum,pos); 32 lol ans=0; 33 if (pos<=n) ans=m*pos; 34 else ans=G[n+1][pos-n-1]; 35 if (kind==0) G[n+1].push_back(ans); 36 else G[n+1].push_back(kind); 37 return ans; 38 } 39 lol zyys2(lol x,lol y) 40 { 41 lol pos=query(root[x],1,sum,y); 42 del(root[x],1,sum,pos); 43 lol ans=0; 44 if (pos<m) ans=(x-1)*m+pos; 45 else ans=G[x][pos-m]; 46 G[x].push_back(zyys1(x,ans)); 47 return ans; 48 } 49 int main() 50 {lol x,y; 51 lol ans; 52 cin>>n>>m>>q; 53 sum=max(n,m)+q; 54 while (q--) 55 { 56 scanf("%lld%lld",&x,&y); 57 if (y==m) ans=zyys1(x,0); 58 else ans=zyys2(x,y); 59 printf("%lld\n",ans); 60 } 61 }