我们考虑每个人离队后对队列的影响,我们可以得出结论,只会对当前行和最后一行产生影响,我们先看在mmm列的特殊情况。
第(i,m)(i,m)(i,m)的人从队伍中离开,跑到了最后一位,我们假设后面的人不往上补充,而是往后多了一个人,这时变成了(n+1,m)(n+1,m)(n+1,m),我们把他放进vectorvectorvector来维护这个序列,这样每次访问只需要pos−npos-npos−n即可
但我们如何找到需要的pospospos呢,权值线段树登场现学
我们维护一个sizesizesize数组,储存这个区域少了几个人,这样我们要找第xxx个人,只需要比较mid−l+1mid-l+1mid−l+1和size[leftson]size[leftson]size[leftson]的大小,就可以判断是
在左儿子找x位在左儿子找x位在左儿子找x位
或在右儿子找第x−(mid−l+1−size[leftson])位或在右儿子找第x-(mid-l+1-size[leftson])位或在右儿子找第x−(mid−l+1−size[leftson])位
得出的pospospos返回即可
这时得出的pospospos,如果在[1,n][1,n][1,n]中,就可以直接通过编号法则得出编号,但如果大于nnn说明是后续入队的,那么他的编号已经在vectorvectorvector中存了,直接输出g[n+1][pos−n−1]g[n+1][pos-n-1]g[n+1][pos−n−1]即可。
注意:这里的寻找边界并非[1,n][1,n][1,n],而是[1,max(n,m)+Q][1,max(n,m)+Q][1,max(n,m)+Q]
思考一下,极限数据下,我们会做出什么畜生事情,执行q次1 m
那么会出现什么情况呢,每一次查询都会在vectorvectorvector里放进去一个数,总共QQQ次,所以我们查询的区间要满足这种极限情况就必须改为[1,max(n,m)+Q][1,max(n,m)+Q][1,max(n,m)+Q]
于是,一个愉快的同理可得,行也解决了
那么思路就出来了,建立n+1棵权值线段树,n棵维护每一行[1,m-1],最后一棵维护最后一列即可
但是会MLEREWAMLE_{RE_{WA}}MLEREWA,那么动态开点就可以了,代码并不难写
可能讲的不太好懂
我们模拟一下上面的lsl畜生过程就好了
这是初始情况,为了好看点就横过来了丑陋Markdown警告
[1234−−−−]\left[
\begin{matrix}
1 & 2 & 3 & 4 &-&-&-&-
\end{matrix}
\right][1234−−−−]
第一次删除(1,2)之后
[1−342−−−]\left[
\begin{matrix}
1 & - & 3 & 4 &2&-&-&-
\end{matrix}
\right][1−342−−−]
紧接下来假如我们还要要删(1,2)呢
我们来看此时的size数组是啥样的
size[1]1size[1]_1size[1]1
size[2]1———————————————size[3]0size[2]_1 ———————————————size[3]_0size[2]1———————————————size[3]0
size[4]1—————size[5]0———————size[6]0——————size[7]0size[4]_1 —————size[5]_0———————size[6]_0 ——————size[7]_0size[4]1—————size[5]0———————size[6]0——————size[7]0
size[8]0−size[9]1−size[10]0−size[11]0−size[12]0−size[13]0−size[14]0−size[15]0size[8]_0 -size[9]_1-size[10]_0 -size[11]_0-size[12]_0 -size[13]_0-size[14]_0 -size[15]_0size[8]0−size[9]1−size[10]0−size[11]0−size[12]0−size[13]0−size[14]0−size[15]0
可以看出,我们一直跑到size[2]size[2]size[2]的时候就会面临第一次选择,我们要找的仍然是2,但是左儿子的大小减去size[4]size[4]size[4]却只有1了,此时就会往右儿子找2−size[4]=12-size[4]=12−size[4]=1了,最后进入size[10]size[10]size[10]发现l=r=3l=r=3l=r=3了,333就是我们要求的位置了;回到上面看一下,没错,可以输出了。
那么再删两次(1,2)呢,会发现第四次删除在原树中已经找不到了,没关系,别忘了1−>maxlen1->maxlen1−>maxlen
看看sizesizesize数组
size[1]3size[1]_3size[1]3
size[2]3———————————————size[3]0size[2]_3 ———————————————size[3]_0size[2]3———————————————size[3]0
size[4]1—————size[5]2———————size[6]0——————size[7]0size[4]_1 —————size[5]_2———————size[6]_0 ——————size[7]_0size[4]1—————size[5]2———————size[6]0——————size[7]0
size[8]0−size[9]1−size[10]1−size[11]1−size[12]0−size[13]0−size[14]0−size[15]0size[8]_0 -size[9]_1-size[10]_1 -size[11]_1-size[12]_0 -size[13]_0-size[14]_0 -size[15]_0size[8]0−size[9]1−size[10]1−size[11]1−size[12]0−size[13]0−size[14]0−size[15]0
这次在size[1]size[1]size[1]的时候,mid−l+1=4mid-l+1=4mid−l+1=4又因为4−size[2]<24-size[2]<24−size[2]<2 就直接奔向size[3]size[3]size[3]找第一位了,于是返回的pospospos为5,大于4了,怎么办,不慌磕瓶药,直接查询vector里的第pos−4pos-4pos−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;
}

本文探讨了在动态队列中,人员离队对队列结构的影响,特别是针对行和列的变化。利用权值线段树进行高效查询与更新,解决了在连续删除操作下队列成员位置追踪的问题。通过实例模拟,详细解析了算法流程,包括如何动态维护大小数组和查询特定位置的成员。
995

被折叠的 条评论
为什么被折叠?



