题目大意
有一个 \(n\times m\) 的方阵,每次有 \((x,y)\) 离开,离开后有两个命令
- 向左看齐。这时第一列保持不动,所有学生向左填补空缺。这条指令之后,空位在第 \(x\) 行第 \(m\) 列。
- 向前看齐。这时第一行保持不动,所有学生向前填补空缺。这条指令之后,空位在第 \(n\) 行第 \(m\) 列。
最后 \((x,y)\) 回到 \((n,m)\)
现在问每次离队的人的编号。 \((i,j)\) 的编号是 \((i-1)*m+j\)
题解
对每一行前 \(m-1\) 个建 \(n\) 个权值线段树,对第 \(m\) 列单独建。
线段树记录区间离开的个数
因为把一个点加入线段树十分麻烦,考虑用 \(n+1\) 个 \(vector\) 来保存加入的数
接着分类讨论
-
\(y==m\),就是只有向前看齐。直接在第 \(n+1\) 颗线段树中 \(x\) 的实际位置 \(pos\)
若 \(pos<=n\) 则没有删除,答案为 \(pos*m\) ,否则在 \(vector\) 中
-
\(y<m\) ,先向左看齐。在第 \(x\) 颗线段树中查询 \(y\) 的实际位置 \(pos\)
若 \(y<m\) 则没有删除,否则在 \(vector\) 中。得到答案再向前看齐
向前看齐加入 \(vector\) 的编号为这里得到的编号
-
每次得到答案就把它加入对应的 \(vector\) 中。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,m,T,pos,maxx,tot,sm[6000000],rt[6000000],ls[6000000],rs[6000000];
vector<LL>del[300005];
int Kth(int k,int l,int r,int rt) {
if(l==r)return l;
register int mid=l+r>>1,tmp=mid-l+1-sm[ls[rt]];
return k<=tmp?Kth(k,l,mid,ls[rt]):Kth(k-tmp,mid+1,r,rs[rt]);
}
void Modify(int p,int l,int r,int &rt) {
if(!rt)rt=++tot; ++sm[rt];
if(l==r)return;
register int mid=l+r>>1;
if(p<=mid)Modify(p,l,mid,ls[rt]);
else Modify(p,mid+1,r,rs[rt]);
}
inline LL Sz(int x,LL y) {
pos=Kth(x,1,maxx,rt[n+1]);
Modify(pos,1,maxx,rt[n+1]);
register LL res;
if(pos<=n)res=1LL*pos*m;
else res=del[n+1][pos-n-1];
del[n+1].push_back(y?y:res);
return res;
}
inline LL Hz(int x,LL y) {
pos=Kth(y,1,maxx,rt[x]);
Modify(pos,1,maxx,rt[x]);
register LL res;
if(pos<m)res=1LL*(x-1)*m+pos;
else res=del[x][pos-m];
del[x].push_back(Sz(x,res));
return res;
}
int main() {
freopen("phalanx.in","r",stdin);
freopen("phalanx.out","w",stdout);
scanf("%d%d%d",&n,&m,&T),maxx=max(n,m)+T;
for(int u,v;T--;) {
scanf("%d%d",&u,&v);
if(v==m)printf("%lld\n",Sz(u,0));
else printf("%lld\n",Hz(u,v));
}
}