您的位置:首页 > 其它

5478. 【NOIP2017提高组正式赛】列队

2017-11-26 09:10 295 查看

题目描述

Sylvia 是一个热爱学习的女孩子。

前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。 Sylvia所在的方阵中有n × m名学生,方阵的行数为 n,列数为 m。

为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中从 1 到 n × m 编上了号码(参见后面的样例)。即:初始时,第 i 行第 j 列的学生的编号是(i − 1) × m + j。

然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天中,一共发生了 q 件这样的离队事件。每一次离队事件可以用数对(y,z) (1≤x≤n,1≤y≤m)描述,表示第 x 行第 y 列的学生离队。

在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达这样的两条指令:

1. 向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条指令之后,空位在第 x 行第 m 列。

2. 向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条指令之后,空位在第 n 行第 m 列。

教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后,下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 n 行第 m 列一个空位,这时这个学生会自然地填补到这个位置。

因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学的编号是多少。

注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后方阵中同学的编号可能是乱序的。

题目分析

这一道题很难。

不过我们发现,整个过程可以总结为2种操作:

1.从序列中找出第k个数并删除。

2.把删除的数加入到序列后面。

这样我们可以用n+1棵线段树来解决。

前n棵是维护每一行的第1列到第m-1列的

第n+1棵是维护第m列的。

关键在于:300000*300000咋整?

只能用动态开点来解决。

如何动态开点?自己看一下代码顺便学习吧。

代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef long long ll;
struct node
{
int x,y;
}a[310000];
struct tree
{
int lc,rc;ll c;
}tr[6100000];int tail[310000],root[310000];
int tot=0;int size[6100000];
void insert(int &now,int l,int r,int k,ll num)
{
if(now==0) now=++tot;
if(l==r)
{
tr[now].c=num;
size[now]=0;
return ;
}
int mid=(l+r)/2;
if(k<=mid) insert(tr[now].lc,l,mid,k,num);
else insert(tr[now].rc,mid+1,r,k,num);
size[now]=size[tr[now].lc]+size[tr[now].rc];
}
ll findkth(int &now,int l,int r,int k,<
4000
span class="hljs-keyword">int &ans)
{
if(now==0) now=++tot;
if(l==r)
{
ans=l;
ll temp=tr[now].c;tr[now].c=0;
size[now]=1;
return temp;
}
int mid=(l+r)/2;ll temp;
if(mid-l+1-size[tr[now].lc]>=k)
{
temp=findkth(tr[now].lc,l,mid,k,ans);
}
else
{
temp=findkth(tr[now].rc,mid+1,r,k-(mid-l+1-size[tr[now].lc]),ans);
}
size[now]=size[tr[now].lc]+size[tr[now].rc];
return temp;
}
int main()
{
ll n,m,q;
scanf("%lld%lld%lld",&n,&m,&q);
for(int i=1;i<=n;i++) tail[i]=m-1;
tail[n+1]=n;
for(int i=1;i<=q;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
if(a[i].y==m)
{
int temp2;
ll temp=findkth(root[n+1],1,n+q,a[i].x,temp2);
if(temp2<=n) temp=temp2*m;
printf("%lld\n",temp);
insert(root[n+1],1,n+q,++tail[n+1],temp);
}
else
{
int temp2;
ll temp=findkth(root[a[i].x],1,m+q,a[i].y,temp2);
if(temp2<m) temp=m*(a[i].x-1)+temp2;
printf("%lld\n",temp);
insert(root[n+1],1,n+q,++tail[n+1],temp);
temp=findkth(root[n+1],1,n+q,a[i].x,temp2);
if(temp2<=n) temp=temp2*m;
insert(root[a[i].x],1,m+q,++tail[a[i].x],temp);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: