【NOIP2017提高組正式賽】列隊

Description

   Sylvia 是一個熱愛學習的女孩子。
   前段時間,Sylvia 參加了學校的軍訓。衆所周知,軍訓的時候需要站方陣。 Sylvia所在的方陣中有n × m名學生,方陣的行數爲 n,列數爲 m。
   爲了便於管理,教官在訓練開始時,按照從前到後,從左到右的順序給方陣中從 1 到 n × m 編上了號碼(參見後面的樣例)。即:初始時,第 i 行第 j 列的學生的編號是(i − 1) × m + j。
   然而在練習方陣的時候,經常會有學生因爲各種各樣的事情需要離隊。在一天中,一共發生了 q 件這樣的離隊事件。每一次離隊事件可以用數對(��,��) (1≤x≤n,1≤y≤m)描述,表示第 x 行第 y 列的學生離隊。
   在有學生離隊後,隊伍中出現了一個空位。爲了隊伍的整齊,教官會依次下達這樣的兩條指令:
   1. 向左看齊。這時第一列保持不動,所有學生向左填補空缺。不難發現在這條指令之後,空位在第 x 行第 m 列。
   2. 向前看齊。這時第一行保持不動,所有學生向前填補空缺。不難發現在這條指令之後,空位在第 n 行第 m 列。
   教官規定不能有兩個或更多學生同時離隊。即在前一個離隊的學生歸隊之後,下一個學生才能離隊。因此在每一個離隊的學生要歸隊時,隊伍中有且僅有第 n 行第 m 列一個空位,這時這個學生會自然地填補到這個位置。
   因爲站方陣真的很無聊,所以 Sylvia 想要計算每一次離隊事件中,離隊的同學的編號是多少。
   注意:每一個同學的編號不會隨着離隊事件的發生而改變,在發生離隊事件後方陣中同學的編號可能是亂序的。

Input

輸入共 q+1 行。
第 1 行包含 3 個用空格分隔的正整數 n, m, q,表示方陣大小是 �� 行 m 列,一共發生了 q 次事件。
接下來 q 行按照事件發生順序描述了 q 件事件。每一行是兩個整數 x, y,用一個空格分隔,表示這個離隊事件中離隊的學生當時排在第 x 行第 y 列。

Output

 按照事件輸入的順序,每一個事件輸出一行一個整數,表示這個離隊事件中離隊學生的編號

Sample Input

【輸入樣例 1】
2 2 3
1 1
2 2
1 2

Sample Output

【輸出樣例 1】
1
1
4

【輸入輸出樣例 1 說明】

  列隊的過程如上圖所示,每一行描述了一個事件。
 在第一個事件中,編號爲 1 的同學離隊,這時空位在第一行第一列。接着所有同學向左標齊,這時編號爲 2 的同學向左移動一步,空位移動到第一行第二列。然後所有同學向上標齊,這時編號爲 4 的同學向上一步,這時空位移動到第二行第二列。最後編號爲 1 的同學返回填補到空位中。

Data Constraint
想法:
權值線段樹+線段樹二分+動態開點
線段樹存幾個值:
x:如果區間長度爲1,存當前位置的值
sum:這個區間有多少個數
l,r:左兒子和右兒子
開n+1棵線段樹,前n個存每行的值(m-1+q)
第n+1棵存第m列的值(n+q)
要做的事很簡單:
y!=m時
把第x行第y個的值找到記爲ans並刪除
把第m列第x個找到並刪除,插入第x行最後一個
把ans插入第m列最後一個那裏
y==m更簡單

Code

#include <cstdio>
#include <cstring>
#include <iostream>
#define ll long long
using namespace std;
const ll maxN=300010;
ll n,m,q,i,len,lem,cnt,p[maxN],ans,sum,sum1,x1,y2,end[maxN];
struct zhj{
    ll x;
    int right,left,sum;
};
zhj tree[18000000];
void ef(ll x,ll head,ll tail,ll xx,ll y){
    ll mid=(head+tail)/2;
    if (head==tail){
        if (tree[x].x==0) {
            if (xx==n+1) tree[x].x=head*m;else tree[x].x=(xx-1)*m+head;
        }
        ans=tree[x].x;
        tree[x].sum=0;
        return;
    }
    mid=(head+tail)/2;
    if (tree[x].left==0){
        tree[x].left=++cnt,tree[cnt].sum=mid-head+1;
    }
    if (tree[x].right==0){
        tree[x].right=++cnt,tree[cnt].sum=tail-mid;
    }
    if (tree[tree[x].left].sum>=y) ef(tree[x].left,head,mid,xx,y);
    else ef(tree[x].right,mid+1,tail,xx,y-tree[tree[x].left].sum);
    tree[x].sum=tree[tree[x].left].sum+tree[tree[x].right].sum;
}
void find(ll x,ll head,ll tail,ll zl,ll l,ll r,ll y){
    if ((l<=head)&&(tail<=r)){
        if (zl==0) tree[x].x=y,tree[x].sum=1;
        if (zl==1) tree[x].sum=tail-head+1;
        return;
    }
    ll mid=(head+tail)/2;
    if (tree[x].left==0){
        tree[x].left=++cnt;
    }
    if (tree[x].right==0){
        tree[x].right=++cnt;
    }
    if (l<=mid) find(tree[x].left,head,mid,zl,l,r,y);
    if (mid<r) find(tree[x].right,mid+1,tail,zl,l,r,y);
    tree[x].sum=tree[tree[x].left].sum+tree[tree[x].right].sum;
}
int main(){
    freopen("phalanx.in","r",stdin);
    freopen("phalanx.out","w",stdout);
    scanf("%lld%lld%lld",&n,&m,&q);
    len=m+q-1,lem=n+q;
    for (i=1;i<=n;i++)
        p[i]=++cnt,tree[cnt].sum=m-1,end[i]=m-1,find(p[i],1,len,1,1,m-1,0);
    p[n+1]=++cnt;
    tree[cnt].sum=n,end[i]=n,find(p[n+1],1,lem,1,1,n,0);
    for (i=1;i<=q;i++){
        scanf("%lld%lld",&x1,&y2);
        if (y2==m){
            ans=0,ef(p[n+1],1,lem,n+1,x1);
            printf("%lld\n",ans);   
            end[n+1]++;
            find(p[n+1],1,lem,0,end[n+1],end[n+1],ans);
            continue;   
        }
        ans=0,ef(p[x1],1,len,x1,y2),sum=ans;
        printf("%lld\n",ans);
        ans=0,ef(p[n+1],1,lem,n+1,x1);
        end[x1]++;
        find(p[x1],1,len,0,end[x1],end[x1],ans);
        end[n+1]++;
        find(p[n+1],1,lem,0,end[n+1],end[n+1],sum);
    }
}
發佈了151 篇原創文章 · 獲贊 71 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章