[NOIp2017 Day2 T3] 列隊phalanx(線段樹 / 平衡樹)

題目

描述

Sylvia 是一個熱愛學習的女♂孩子。
前段時間,Sylvia 參加了學校的軍訓。衆所周知,軍訓的時候需要站方陣。
Sylvia 所在的方陣中有n×m 名學生,方陣的行數爲 n ,列數爲 m
爲了便於管理,教官在訓練開始時,按照從前到後,從左到右的順序給方陣中 的學生從 1 到 n×m 編上了號碼(參見後面的樣例)。即:初始時,第 i 行第 j 列 的學生的編號是(i1)×m+j
然而在練習方陣的時候,經常會有學生因爲各種各樣的事情需要離隊。在一天 中,一共發生了 q 件這樣的離隊事件。每一次離隊事件可以用數對(x,y)(1xn,1ym) 描述,表示第 x 行第 y 列的學生離隊。
在有學生離隊後,隊伍中出現了一個空位。爲了隊伍的整齊,教官會依次下達 這樣的兩條指令:

  1. 向左看齊。這時第一列保持不動,所有學生向左填補空缺。不難發現在這條 指令之後,空位在第 x 行第 m 列。
  2. 向前看齊。這時第一行保持不動,所有學生向前填補空缺。不難發現在這條 指令之後,空位在第 n 行第 m 列。

教官規定不能有兩個或更多學生同時離隊。即在前一個離隊的學生歸隊之後, 下一個學生才能離隊。因此在每一個離隊的學生要歸隊時,隊伍中有且僅有第 n 行 第 m 列一個空位,這時這個學生會自然地填補到這個位置。
因爲站方陣真的很無聊,所以 Sylvia 想要計算每一次離隊事件中,離隊的同學 的編號是多少。
注意:每一個同學的編號不會隨着離隊事件的發生而改變,在發生離隊事件後 方陣中同學的編號可能是亂序的。

輸入

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

輸出

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

輸入樣例

2 2 3 
1 1 
2 2 
1 2

輸出樣例

1
1
4

數據規模與約定

n,m,q3×105
數據保證每一個事件滿足 1xn,1ym


解題思路

一道數據結構的好題。
首先我們發現,每次操作只會更改某一行和最後一列的狀態,那麼我們可以單獨把最後一列拿出來用一個數據結構維護,再用n 個數據結構維護每一行的前m1 個元素。
那用什麼數據結構好呢?

一、線段樹

線段樹是最容易想到的,共開n+1 顆線段樹,前n 顆維護每行前m1 個元素,第n+1 顆維護最後一列的元素。
每次對(x,y) 操作都可以轉化爲一個基本操作:從一顆線段樹裏面拿出一個元素加到一顆線段樹的末尾,具體來說:

  • 如果y=m ,只需要從“列線段樹”裏拿出x 個元素加到它本身末尾
  • 否則,從第x 顆“行線段樹”裏拿出y 個元素加到“列線段樹”末尾,再從“列線段樹”裏拿出x 個元素加到“行線段樹”末尾

所謂的“拿出”操作就是一個在線段樹上二分查找的過程,爲此我們要在線段樹每個節點上記錄一個size,表示當前節點表示的區間裏面還剩多少個元素
另外,每顆線段樹要多開q 的區間長度(想想操作過程就明白了)。

但是,以上並不是這道題的難點,這道題的特殊之處在於你無法直接開滿n+1 顆線段樹!
怎麼辦呢,我們可以動態開點來解決,也就是說當你要用某個點時再開它(想想主席樹)。這樣我們只需要NlogN 的空間就夠了。

時間複雜度 O(qlog(n+q))

二、平衡樹

既然線段樹可以,平衡樹當然也可以了!
同樣的思路:每次操作都可以轉化爲從一顆平衡樹上二分查找第k大的值,把它加到一顆平衡樹的末尾。

怎麼解決空間問題?由於有一些人至始至終都站在一起,我們可以在平衡樹上只用一個節點表示這個區間[l,r] (編號從lr 的人),當我們發現這個區間中的某個人(如編號爲k 的人)要離隊時,再把它split成兩個小區間([l,k1],[k+1,r] ),輸出k ,這樣就能保證空間複雜度爲 NlogN

時間複雜度O(qlogn)

三、樹狀數組

有待學習…


Code#1(線段樹)

#include<cstdio>
#include<algorithm>

using namespace std;

typedef long long LL;

const int N = 300005;
int n, m, q, qx, qy, p[N], root[N];
LL t;

struct segTree{
    int son[2];
    LL val, size;
}tr[N*30];

struct OPT_segTree{
    int cnt;
    inline int newNode(int l, int r, int kind){
        cnt++;
        int temp = (kind == 0 ? m - 1 : n);
        if(l <= temp && r <= temp)  tr[cnt].size = r - l + 1;
        else if(l <= temp && r > temp)      tr[cnt].size = temp - l + 1;
        else if(l > temp && r > temp)       tr[cnt].size = 0;
        return cnt;
    }
    inline void pushup(int id){
        tr[id].size = tr[tr[id].son[0]].size + tr[tr[id].son[1]].size;
    }
    LL getKth(int id, int l, int r, LL k, int kind){
        if(l == r){
            if(!tr[id].val){
                if(kind == 0)   tr[id].val = 1ll * (qx - 1) * m + l;
                else    tr[id].val = 1ll * l * m;
            }
            tr[id].size = 0;
            return tr[id].val;
        }
        int mid = (l + r) >> 1;
        if(!tr[id].son[0])  tr[id].son[0] = newNode(l, mid, kind);
        if(!tr[id].son[1])  tr[id].son[1] = newNode(mid+1, r, kind);
        LL res = 0;
        if(tr[tr[id].son[0]].size >= k) res = getKth(tr[id].son[0], l, mid, k, kind);
        else    res = getKth(tr[id].son[1], mid+1, r, k - tr[tr[id].son[0]].size, kind);
        pushup(id);
        return res;
    }
    void insert(int id, int l, int r, int pos, LL val, int kind){
        if(l == r){
            tr[id].val = val;
            tr[id].size = 1;
            return;
        }
        int mid = (l + r) >> 1;
        if(!tr[id].son[0])  tr[id].son[0] = newNode(l, mid, kind);
        if(!tr[id].son[1])  tr[id].son[1] = newNode(mid+1, r, kind);
        if(pos <= mid)  insert(tr[id].son[0], l, mid, pos, val, kind);
        else    insert(tr[id].son[1], mid+1, r, pos, val, kind);
        pushup(id);
    }
}Seg;

int main(){
    scanf("%d%d%d", &n, &m, &q);
    for(int i = 1; i <= n; i++) p[i] = m - 1;
    p[n+1] = n;
    for(int i = 1; i <= n; i++) root[i] = Seg.newNode(1, m - 1 + q, 0);
    root[n+1] = Seg.newNode(1, n + q, 1);
    for(int i = 1; i <= q; i++){
        scanf("%d%d", &qx, &qy);
        if(qy == m){
            printf("%lld\n", t = Seg.getKth(root[n+1], 1, n + q, qx, 1));
            Seg.insert(root[n+1], 1, n + q, ++p[n+1], t, 1);
        }
        else{
            printf("%lld\n", t = Seg.getKth(root[qx], 1, m - 1 + q, qy, 0));
            Seg.insert(root[n+1], 1, n + q, ++p[n+1], t, 1);
            t = Seg.getKth(root[n+1], 1, n + q, qx, 1);
            Seg.insert(root[qx], 1, m - 1 + q, ++p[qx], t, 0);
        }
    }
    return 0;
}

Code#2(Splay)

#include<cstdio>

using namespace std;

typedef long long LL;

const int N = 300005;
int qx, qy, rt[N];
LL t, n, m, q;

struct Splay{
    int son[2], fa;
    LL val, l, r, size;
}tr[N*30];

struct OPT_Splay{
    int cnt;
    inline void pushup(int id){
        tr[id].size = tr[id].r - tr[id].l + 1;
        if(tr[id].son[0])   tr[id].size += tr[tr[id].son[0]].size;
        if(tr[id].son[1])   tr[id].size += tr[tr[id].son[1]].size;
    }
    inline int newNode(LL l, LL r){
        cnt++;
        tr[cnt].fa = tr[cnt].son[0] = tr[cnt].son[1] = 0;
        tr[cnt].size = (tr[cnt].r = r) - (tr[cnt].l = l) + 1;
        return cnt;
    }
    inline void rotate(int x, int kind){
        int y = tr[x].fa, z = tr[y].fa, A = tr[y].son[kind], B = tr[x].son[kind], C = tr[x].son[!kind];
        tr[x].son[kind] = y, tr[x].fa = z;
        tr[y].fa = x, tr[y].son[!kind] = B;
        tr[z].son[tr[z].son[1] == y] = x, tr[B].fa = y;
        pushup(y), pushup(x);
    }
    inline void splay(int &root, int x, int goal){
        if(x == goal)   return;
        while(tr[x].fa != goal){
            int y = tr[x].fa, z = tr[y].fa;
            int isrson1 = tr[y].son[1] == x, isrson2 = tr[z].son[1] == y;
            if(z == goal)   rotate(x, !isrson1);
            else{
                if(isrson1 == isrson2)  rotate(y, !isrson2);
                else    rotate(x, !isrson1);
                rotate(x, !isrson2);
            }
        }
        if(goal == 0)   root = x;
    }
    inline int selectLast(int &root){
        int now = root;
        while(tr[now].son[1])   now = tr[now].son[1];
        return now;
    }
    inline void insert(int &root, LL val){
        int temp = newNode(val, val);
        int pos = selectLast(root);
        tr[pos].son[1] = temp;
        tr[temp].fa = pos;
        splay(root, temp, 0);
    }
    LL split(int &root, int now, LL k){
        splay(root, now, 0);
        k += tr[now].l - 1;
        int temp = newNode(k+1, tr[now].r);
        tr[now].r = k - 1;
        if(!tr[now].son[1]){
            tr[now].son[1] = temp;
            tr[temp].fa = now;
        }
        else{
            tr[temp].son[1] = tr[now].son[1];
            tr[tr[temp].son[1]].fa = temp;
            tr[now].son[1] = temp;
            tr[temp].fa = now;
        }
        pushup(temp), pushup(now);
        return k;
    }
    inline LL getKth(int &root, LL k){
        int now = root;
        while(1){
            if(k <= tr[tr[now].son[0]].size)    now = tr[now].son[0];
            else{
                k -= tr[tr[now].son[0]].size;
                if(k <= tr[now].r - tr[now].l + 1)
                    return split(root, now, k);
                else{
                    k -= (tr[now].r - tr[now].l + 1);
                    now = tr[now].son[1];
                }
            }
        }
    }
}BST;

int main(){
    scanf("%lld%lld%lld", &n, &m, &q);
    for(int i = 1; i <= n; i++) rt[i] = BST.newNode((i - 1) * m + 1, i * m - 1);
    rt[n+1] = BST.newNode(m, m);
    for(int i = 2; i <= n; i++) BST.insert(rt[n+1], i * m);
    while(q--){
        scanf("%d%d", &qx, &qy);
        if(qy == m){
            printf("%lld\n", t = BST.getKth(rt[n+1], qx));
            BST.insert(rt[n+1], t);
        }
        else{
            printf("%lld\n", t = BST.getKth(rt[qx], qy));
            BST.insert(rt[n+1], t);
            BST.insert(rt[qx], BST.getKth(rt[n+1], qx));
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章