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

来源:互联网 发布:淘宝明星店铺 编辑:程序博客网 时间:2024/06/06 18:49

题目描述

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,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;}
阅读全文
0 0