1. 程式人生 > >洛谷P3960 列隊

洛谷P3960 列隊

namespace 亂序 插入 由於 cst 建立 原來 輸入輸出格式 正整數

洛谷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;
}

洛谷P3960 列隊