1. 程式人生 > >JZOJ 5478. 【NOIP2017提高組正式賽】列隊

JZOJ 5478. 【NOIP2017提高組正式賽】列隊

Description

   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 想要計算每一次離隊事件中,離隊的同學的編號是多少。
   注意:每一個同學的編號不會隨著離隊事件的發生而改變,在發生離隊事件後方陣中同學的編號可能是亂序的。

Input

輸入共 q+1 行。
第 1 行包含 3 個用空格分隔的正整數 n, m, q,表示方陣大小是 n 行 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 說明】

Sample Output

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

Data Constraint

Data Constraint

Solution

  • 對於每一個操作 (x,y) , 相當於把第 xy+1 列到 m 列左移,

  • 再把第 m 列第 x+1 行到第 n 行上移,把 (x,y) 移到 (n,m)

  • 如果每一行只看前 m1 列的數,第 m

    列單獨看 1n 行的數。

  • 那麼問題可以轉換為一種查詢兩種操作:

    1. 找到第 k 大的數並把它刪掉;

    2. 加一個數在序列後面。

  • 注意到加入只是加入到 序列的末尾,且加入數300000

  • 所以可以用線段樹預留 300000 位置直接維護,查詢用 線段樹二分

  • 但是空間限制不允許把所有的東西存下來,所以用 動態開節點 的方法來維護線段樹。

  • 動態開節點也有可能爆空間(甚至是時間),所以可以預先求出每一行要開的大小,

  • 這樣就不用開到 600000 ,均攤下來還是 300000

  • 時間複雜度 O(Qlog(N+Q))

Code

#include<cstdio>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=6e6+5;
int tot,pos;
int size[N],s[N][2],len[N],rt[N];
LL key[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline void write(LL x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline void update(int v)
{
    size[v]=size[s[v][0]]+size[s[v][1]];
}
inline LL kth(int &v,int l,int r,int x)
{
    if(!v) v=++tot;
    if(l==r)
    {
        size[v]=1,pos=l;
        return key[v];
    }
    int mid=l+r>>1,wz=mid-l+1-size[s[v][0]];
    LL y=wz>=x?kth(s[v][0],l,mid,x):kth(s[v][1],mid+1,r,x-wz);
    update(v);
    return y;
}
inline void change(int &v,int l,int r,int x,LL y)
{
    if(!v) v=++tot;
    if(l==r)
    {
        key[v]=y;
        return;
    }
    int mid=l+r>>1;
    if(x<=mid) change(s[v][0],l,mid,x,y); else change(s[v][1],mid+1,r,x,y);
    update(v);
}
int main()
{
    int n=read(),m=read(),q=read();
    for(int i=1;i<=n;i++) len[i]=m-1;
    len[n+1]=n;
    for(int i=1;i<=q;i++)
    {
        int x=read(),y=read();
        if(y==m)
        {
            LL num=kth(rt[n+1],1,n+q,x);
            if(pos<=n) num=(LL)pos*m;
            write(num),putchar('\n');
            change(rt[n+1],1,n+q,++len[n+1],num);
        }else
        {
            LL num=kth(rt[x],1,m+q,y);
            if(pos<m) num=(LL)(x-1)*m+pos;
            write(num),putchar('\n');
            change(rt[n+1],1,n+q,++len[n+1],num);
            num=kth(rt[n+1],1,n+q,x);
            if(pos<=n) num=(LL)pos*m;
            change(rt[x],1,m+q,++len[x],num);
        }
    }
    return 0;
}