洛谷P3960 列队

洛谷P3960 列队

题目描述

Sylvia 是一个热爱学习的女孩子。
前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。
Sylvia 所在的方阵中有\(n \times m\)名学生,方阵的行数为 \(n\),列数为 \(m\)
为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 \(1\)\(n \times m\) 编上了号码(参见后面的样例)。即:初始时,第 \(i\) 行第 \(j\) 列 的学生的编号是\((i-1)\times m + j\)
然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天 中,一共发生了 $q \(件这样的离队事件。每一次离队事件可以用数对\)(x,y) (1 \leq x \leq n, 1 \leq y \leq m)$描述,表示第 \(x\) 行第 \(y\) 列的学生离队。
在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:

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

教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后, 下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 \(n\) 行 第 \(m\) 列一个空位,这时这个学生会自然地填补到这个位置。
因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学的编号是多少。
注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后方阵中同学的编号可能是乱序的。

输入输出格式

输入格式:

输入共 \(q+1\) 行。
第 1 行包含 3 个用空格分隔的正整数 \(n, m, q\),表示方阵大小是 \(n\)\(m\) 列,一共发 生了 \(q\) 次事件。
接下来 \(q\) 行按照事件发生顺序描述了 \(q\) 件事件。每一行是两个整数 \(x, y\),用一个空 格分隔,表示这个离队事件中离队的学生当时排在第 \(x\) 行第 \(y\) 列。

输出格式:

按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学生的编号。

思路

可以发现每一次离队只会影响离队者所在的这一排和最后一列
于是我们建立\(n+1\)棵线段树,前\(n\)棵维护每一行的前\(m-1\)列,最后一棵维护最后一列。
每一棵线段树维护区间和。
由于最多会进行\(q\)次操作,因此每一行最多会进行\(q\)次更新,故线段树只需要开到\(m+q\),如果使用动态开点的话是可以维护的。
对于每一次离队操作,如果在前\(m-1\)列,那么将这一次离队者在线段树中的位置的值设为\(0\),然后将第\(n+1\)棵线段树中这一行对应的位置的值设为\(0\),并将它添加到离队者原来所在的线段树的末尾,然后将离队者插入第\(m+1\)棵线段树的末尾。

CODE

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 10000010
typedef long long int lli;
int ch[MAXN][2],sz[MAXN],rt[MAXN],nums[MAXN];
lli val[MAXN];
int i,j,k,m,n,x,y,ncnt,maxn;
char readc;
void read(int &n){
    while((readc=getchar())<48||readc>57);
    n=readc-48;
    while((readc=getchar())>=48&&readc<=57) n=n*10+readc-48;
}
int getsize(int id,int l,int r){
    if(id<=n){
        if(r<m) return r-l+1;
        if(l<m) return m-l;
        return 0;
    }
    if(r<=n) return r-l+1;
    if(l<=n) return n-l+1;
    return 0;
}
void update(int id,int &rt,int l,int r,int p,lli upd){
    if(!rt){
        rt=++ncnt;
        ch[rt][0]=ch[rt][1]=0;
        sz[rt]=getsize(id,l,r);
        if(l==r) val[rt]=upd;
    }
    sz[rt]++;
    if(l==r){
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid) update(id,ch[rt][0],l,mid,p,upd);
    else update(id,ch[rt][1],mid+1,r,p,upd);
}
lli query(int id,int &rt,int l,int r,int p){
    if(!rt){
        rt=++ncnt;
        ch[rt][0]=ch[rt][1]=0;
        sz[rt]=getsize(id,l,r);
        if(l==r){
            if(id<=n){
                val[rt]=(lli) (id-1)*m+l;
            }else val[rt]=(lli) l*m;
        }
    }
    sz[rt]--;
    if(l==r) {
        return val[rt];
    }
    int mid=(l+r)>>1;
    if(ch[rt][0]){
        if(p<=sz[ch[rt][0]]){
            return query(id,ch[rt][0],l,mid,p);
        }else{
            return query(id,ch[rt][1],mid+1,r,p-sz[ch[rt][0]]);
        }
    }else{
        if(p<=mid-l+1){
            return query(id,ch[rt][0],l,mid,p);
        }else{
            return query(id,ch[rt][1],mid+1,r,p-(mid-l+1));
        }
    }
    return 0;
}
int main(){
    read(n),read(m),read(k);
    ncnt=0;
    for(i=1;i<=n;i++) nums[i]=m-1;
    nums[n+1]=n;
    maxn=max(n,m)+k;
    for(i=1;i<=k;i++){
        read(x),read(y);
        lli tmp;
        if(y<m){
            tmp=query(x,rt[x],1,maxn,y);
            update(n+1,rt[n+1],1,maxn,++nums[n+1],tmp);
            printf("%lld\n",tmp);
            tmp=query(n+1,rt[n+1],1,maxn,x);
            update(x,rt[x],1,maxn,++nums[x],tmp);
        }else{
            tmp=query(n+1,rt[n+1],1,maxn,x);
            update(n+1,rt[n+1],1,maxn,++nums[n+1],tmp);
            printf("%lld\n",tmp);
        }
    }
    return 0;
}
posted @ 2019-02-02 00:25  EternalBlue  阅读(208)  评论(0编辑  收藏  举报