我们考虑每个人离队后对队列的影响,我们可以得出结论,只会对当前行和最后一行产生影响,我们先看在 m m m列的特殊情况。
第 ( i , m ) (i,m) (i,m)的人从队伍中离开,跑到了最后一位,我们假设后面的人不往上补充,而是往后多了一个人,这时变成了 ( n + 1 , m ) (n+1,m) (n+1,m),我们把他放进 v e c t o r vector vector来维护这个序列,这样每次访问只需要 p o s − n pos-n pos−n即可
但我们如何找到需要的
p
o
s
pos
pos呢,权值线段树登场现学
我们维护一个
s
i
z
e
size
size数组,储存这个区域少了几个人,这样我们要找第
x
x
x个人,只需要比较
m
i
d
−
l
+
1
mid-l+1
mid−l+1和
s
i
z
e
[
l
e
f
t
s
o
n
]
size[leftson]
size[leftson]的大小,就可以判断是
在
左
儿
子
找
x
位
在左儿子找x位
在左儿子找x位
或
在
右
儿
子
找
第
x
−
(
m
i
d
−
l
+
1
−
s
i
z
e
[
l
e
f
t
s
o
n
]
)
位
或在右儿子找第x-(mid-l+1-size[leftson])位
或在右儿子找第x−(mid−l+1−size[leftson])位
得出的
p
o
s
pos
pos返回即可
这时得出的 p o s pos pos,如果在 [ 1 , n ] [1,n] [1,n]中,就可以直接通过编号法则得出编号,但如果大于 n n n说明是后续入队的,那么他的编号已经在 v e c t o r vector vector中存了,直接输出 g [ n + 1 ] [ p o s − n − 1 ] g[n+1][pos-n-1] g[n+1][pos−n−1]即可。
注意:这里的寻找边界并非 [ 1 , n ] [1,n] [1,n],而是 [ 1 , m a x ( n , m ) + Q ] [1,max(n,m)+Q] [1,max(n,m)+Q]
思考一下,极限数据下,我们会做出什么畜生事情,执行q次1 m
那么会出现什么情况呢,每一次查询都会在
v
e
c
t
o
r
vector
vector里放进去一个数,总共
Q
Q
Q次,所以我们查询的区间要满足这种极限情况就必须改为
[
1
,
m
a
x
(
n
,
m
)
+
Q
]
[1,max(n,m)+Q]
[1,max(n,m)+Q]
于是,一个愉快的同理可得,行也解决了
那么思路就出来了,建立n+1棵权值线段树,n棵维护每一行[1,m-1],最后一棵维护最后一列即可
但是会
M
L
E
R
E
W
A
MLE_{RE_{WA}}
MLEREWA,那么动态开点就可以了,代码并不难写
可能讲的不太好懂
我们模拟一下上面的lsl畜生过程就好了
这是初始情况,为了好看点就横过来了丑陋Markdown警告
[
1
2
3
4
−
−
−
−
]
\left[ \begin{matrix} 1 & 2 & 3 & 4 &-&-&-&- \end{matrix} \right]
[1234−−−−]
第一次删除(1,2)之后
[
1
−
3
4
2
−
−
−
]
\left[ \begin{matrix} 1 & - & 3 & 4 &2&-&-&- \end{matrix} \right]
[1−342−−−]
紧接下来假如我们还要要删(1,2)呢
我们来看此时的size数组是啥样的
s
i
z
e
[
1
]
1
size[1]_1
size[1]1
s
i
z
e
[
2
]
1
—
—
—
—
—
—
—
—
—
—
—
—
—
—
—
s
i
z
e
[
3
]
0
size[2]_1 ———————————————size[3]_0
size[2]1———————————————size[3]0
s
i
z
e
[
4
]
1
—
—
—
—
—
s
i
z
e
[
5
]
0
—
—
—
—
—
—
—
s
i
z
e
[
6
]
0
—
—
—
—
—
—
s
i
z
e
[
7
]
0
size[4]_1 —————size[5]_0———————size[6]_0 ——————size[7]_0
size[4]1—————size[5]0———————size[6]0——————size[7]0
s
i
z
e
[
8
]
0
−
s
i
z
e
[
9
]
1
−
s
i
z
e
[
10
]
0
−
s
i
z
e
[
11
]
0
−
s
i
z
e
[
12
]
0
−
s
i
z
e
[
13
]
0
−
s
i
z
e
[
14
]
0
−
s
i
z
e
[
15
]
0
size[8]_0 -size[9]_1-size[10]_0 -size[11]_0-size[12]_0 -size[13]_0-size[14]_0 -size[15]_0
size[8]0−size[9]1−size[10]0−size[11]0−size[12]0−size[13]0−size[14]0−size[15]0
可以看出,我们一直跑到
s
i
z
e
[
2
]
size[2]
size[2]的时候就会面临第一次选择,我们要找的仍然是2,但是左儿子的大小减去
s
i
z
e
[
4
]
size[4]
size[4]却只有1了,此时就会往右儿子找
2
−
s
i
z
e
[
4
]
=
1
2-size[4]=1
2−size[4]=1了,最后进入
s
i
z
e
[
10
]
size[10]
size[10]发现
l
=
r
=
3
l=r=3
l=r=3了,
3
3
3就是我们要求的位置了;回到上面看一下,没错,可以输出了。
那么再删两次(1,2)呢,会发现第四次删除在原树中已经找不到了,没关系,别忘了
1
−
>
m
a
x
l
e
n
1->maxlen
1−>maxlen
看看
s
i
z
e
size
size数组
s
i
z
e
[
1
]
3
size[1]_3
size[1]3
s
i
z
e
[
2
]
3
—
—
—
—
—
—
—
—
—
—
—
—
—
—
—
s
i
z
e
[
3
]
0
size[2]_3 ———————————————size[3]_0
size[2]3———————————————size[3]0
s
i
z
e
[
4
]
1
—
—
—
—
—
s
i
z
e
[
5
]
2
—
—
—
—
—
—
—
s
i
z
e
[
6
]
0
—
—
—
—
—
—
s
i
z
e
[
7
]
0
size[4]_1 —————size[5]_2———————size[6]_0 ——————size[7]_0
size[4]1—————size[5]2———————size[6]0——————size[7]0
s
i
z
e
[
8
]
0
−
s
i
z
e
[
9
]
1
−
s
i
z
e
[
10
]
1
−
s
i
z
e
[
11
]
1
−
s
i
z
e
[
12
]
0
−
s
i
z
e
[
13
]
0
−
s
i
z
e
[
14
]
0
−
s
i
z
e
[
15
]
0
size[8]_0 -size[9]_1-size[10]_1 -size[11]_1-size[12]_0 -size[13]_0-size[14]_0 -size[15]_0
size[8]0−size[9]1−size[10]1−size[11]1−size[12]0−size[13]0−size[14]0−size[15]0
这次在
s
i
z
e
[
1
]
size[1]
size[1]的时候,
m
i
d
−
l
+
1
=
4
mid-l+1=4
mid−l+1=4又因为
4
−
s
i
z
e
[
2
]
<
2
4-size[2]<2
4−size[2]<2 就直接奔向
s
i
z
e
[
3
]
size[3]
size[3]找第一位了,于是返回的
p
o
s
pos
pos为5,大于4了,怎么办,不慌磕瓶药,直接查询vector里的第
p
o
s
−
4
pos-4
pos−4位即可
所以总的算法流程结束了,动态开点啥的对着代码感性理解一下就好了
应该算讲清楚了吧…
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 300007;
const int maxm = 10000007;
typedef long long ll;
int maxlen,n,m,q,rt[maxn],lc[maxm],rc[maxm],size[maxm];
vector<ll>g[maxn];
int cnt;
int query(int l,int r,int now,int x)
{
if(l==r)return l;
int mid=l+r>>1,h=mid-l+1-size[lc[now]];
if(x<=h)return query(l,mid,lc[now],x);
else return query(mid+1,r,rc[now],x-h);
}
void del(int l,int r,int &now,int x)
{
if(!now)now=++cnt;
size[now]++;
if(l==r)return;
int mid=l+r>>1;
if(x<=mid)del(l,mid,lc[now],x);
else del(mid+1,r,rc[now],x);
}
ll del_line(int x,int y)
{
int pos=query(1,maxlen,rt[x],y);
del(1,maxlen,rt[x],pos);
if(pos<m)
{
return 1ll*(x-1)*m+pos;
}
else return g[x][pos-m];
}
ll del_row(int x)
{
int pos=query(1,maxlen,rt[n+1],x);
del(1,maxlen,rt[n+1],pos);
if(pos<=n)
{
return 1ll*pos*m;
}
else return g[n+1][pos-n-1];
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
maxlen=max(n,m)+q+1;
for(int i=1;i<=q;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(y==m)
{
ll hang=del_row(x);
g[n+1].push_back(hang);
printf("%lld\n",hang);
}
else
{
ll lie=del_line(x,y);
g[n+1].push_back(lie);
ll hang=del_row(x);
g[x].push_back(hang);
printf("%lld\n",lie);
}
}
return 0;
}