洛谷 P3960 列队
Sylvia 是一个热爱学习的女孩子。
前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。
Sylvia 所在的方阵中有\(n\times m\)名学生,方阵的行数为n,列数为m。
为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中的学生从1到\(n\times m\)编上了号码(参见后面的样例)。即:初始时,第i行第j列的学生的编号是\((i-1)\times m+j\)。
然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天中,一共发生了q件这样的离队事件。每一次离队事件可以用数对\((x,y) (1 \le x \le n, 1 \le y \le m)\)描述,表示第x行第y列的学生离队。
在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达这样的两条指令:
向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条指令之后,空位在第x行第m列。
向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条指令之后,空位在第n行第m列。
教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后,下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第n行第m列一个空位,这时这个学生会自然地填补到这个位置。
因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学的编号是多少。
注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后方阵中同学的编号可能是乱序的。
考虑用线段树计算每一行第x个人的位置,因为n和m都很大,所以要动态开点,线段树的最长长度就是\(max(n,m)+p\)
计算第x人的位置很好算,维护一个区间内有多少个人被删除掉,查询的时候用类似于主席树的方法查询就可以了
这样删除操作也就说完了
而添加时我们开n+1个vector,1~n维护前m-1个人,第n+1维护最后一列,这是因为我们每次操作是先左移再上移
对于一对(x,y),如果\(y=m\),则只需要上移,所以直接在第n+1个vector里操作然后把这个数丢到vector最后就好了;否则就要先左移,需要查出第x个vector的第y个,删掉后丢到vector最后,然后上移,在第n+1个vector进行类似的操作
然后算编号也是很好算的,左移操作中如果我们得到的位置\(ans <= m-1\),那么编号为\((x-1)*n+ans\),否则为vector中第\([x][ans-m]\)个数;上移操作中如果\(ans <= n\),那么编号为\(ans*m\),否则为vector中第\([n+1][ans-n-1]\)个数(vector下标是从0开始的)
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#define LL long long
const int N = 3e5;
using namespace std;
int n,m,q,rt[N + 5],lc[N * 20 + 5],rc[N * 20 + 5],len,node_cnt;
vector <LL> d[N + 5];
struct Seg
{
int del[N * 20 + 5];
int query(int k,int l,int r,int x)
{
if (l == r)
return l;
int mid = l + r >> 1,y = mid - l + 1 - del[lc[k]];
if (y >= x)
return query(lc[k],l,mid,x);
else
return query(rc[k],mid + 1,r,x - y);
}
void change(int &k,int l,int r,int x)
{
if (!k)
k = ++node_cnt;
if (l == r)
{
del[k]++;
return;
}
int mid = l + r >> 1;
if (x <= mid)
change(lc[k],l,mid,x);
else
change(rc[k],mid + 1,r,x);
del[k] = del[lc[k]] + del[rc[k]];
}
LL ask1(int x,LL y)
{
LL ans = query(rt[n + 1],1,len,x);
change(rt[n + 1],1,len,ans);
if (ans <= n)
ans *= 1ll * m;
else
ans = d[n + 1][ans - n - 1];
d[n + 1].push_back(y ? y : ans);
return ans;
}
LL ask2(int x,int y)
{
LL ans = query(rt[x],1,len,y);
change(rt[x],1,len,ans);
if (ans <= m - 1)
ans = 1ll * (x - 1) * m + ans;
else
ans = d[x][ans - m];
d[x].push_back(ask1(x,ans));
return ans;
}
}tree;
int main()
{
scanf("%d%d%d",&n,&m,&q);
len = max(n,m) + q;
int x,y;
for (int i = 1;i <= q;i++)
{
scanf("%d%d",&x,&y);
if (y == m)
printf("%lld\n",tree.ask1(x,0));
else
printf("%lld\n",tree.ask2(x,y));
}
return 0;
}