【NOIP2017D2T3】列隊

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

線段樹

好像是第一次寫這種線段樹維護區間插入/刪除的題(因爲聽說splay常數大,不敢寫)。
首先肯定將第m列與那n行分開維護,一共有n+1顆線段樹。
我們首先將對應的節點打上標記,表示該節點並沒有改變,當我們需要訪問其子區間的時候,將該區間拆成兩個。然後詢問(x,y)(x,y)的時候,假設y!=my!=m,那麼就是找第x行的第y個數。否則就是找第m行的第x個數。找數的過程就是線段樹上二分,具體來說我們對每個區間要記錄一個sizesize表示區間內的人數。然後在將對應區間的sizesize減少。插入的時候直接插入在區間的最後就行了。
每一行最多n+mn+m個點,所以動態開點上界設在n+mn+m就可以了。

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<ctime>
#include<queue>
#include<iomanip>
#define ll long long
#define N 300005

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

ll n,m,q;
int cnt;
int nxt[N];
const int lx=1,rx=N<<1;
int rt[N];
int tag[N*70],ls[N*70],rs[N*70],size[N*70];
ll id[N*70];
void update(int v) {size[v]=size[ls[v]]+size[rs[v]];}

void build(int &v,int l,int r) {
	if(!v) v=++cnt;
	size[v]=r-l+1;
	if(l==r) id[v]=l;
	tag[v]=1;
}

void pre(int &v,int l,int r,int lx,int rx) {
	if(!v) v=++cnt;
	if(l>rx||r<lx) return ;
	if(l<=lx&&rx<=r) {build(v,lx,rx);return ;}
	int mid=lx+rx>>1;
	pre(ls[v],l,r,lx,mid);
	pre(rs[v],l,r,mid+1,rx);
	update(v);
}

void Find(int v,int lx,int rx,int k,ll &pos,ll &g) {
	if(lx==rx) {
		pos=lx;
		g=id[v];
		return ;
	}
	int mid=lx+rx>>1;
	if(tag[v]) {
		build(ls[v],lx,mid);
		build(rs[v],mid+1,rx);
		tag[v]=0;
	}
	if(k>size[ls[v]]) {
		Find(rs[v],mid+1,rx,k-size[ls[v]],pos,g);
	} else {
		Find(ls[v],lx,mid,k,pos,g);
	}
}

void Delete(int v,int lx,int rx,int pos) {
	size[v]--;
	if(lx==rx) return ;
	int mid=lx+rx>>1;
	if(tag[v]) {
		build(ls[v],lx,mid);
		build(rs[v],mid+1,rx);
		tag[v]=0;
	}
	if(pos<=mid) Delete(ls[v],lx,mid,pos);
	else Delete(rs[v],mid+1,rx,pos);
}

void Insert(int &v,int lx,int rx,int pos,ll id) {
	if(!v) v=++cnt;
	size[v]++;
	if(lx==rx) {
		::id[v]=id;
		return ;
	}
	int mid=lx+rx>>1;
	if(tag[v]) {
		build(ls[v],lx,mid);
		build(rs[v],mid+1,rx);
		tag[v]=0;
	}
	if(pos<=mid) Insert(ls[v],lx,mid,pos,id);
	else Insert(rs[v],mid+1,rx,pos,id);
}

ll pos,g;

void Get_out(ll &nx,ll &ny,ll x,ll y) {
	if(y<m) {
		Find(rt[x],lx,rx,y,pos,g);
		if(pos<=m-1) {
			nx=x;
			ny=pos;
		} else {
			nx=(g-1)/m+1;
			ny=(g-1)%m+1;
		}
		Delete(rt[x],lx,rx,pos);
	} else {
		Find(rt[n+1],lx,rx,x,pos,g);
		if(pos<=n) {
			nx=pos;
			ny=m;
		} else {
			nx=(g-1)/m+1;
			ny=(g-1)%m+1;
		}
		Delete(rt[n+1],lx,rx,pos);
	}
}

int main() {
	n=Get(),m=Get(),q=Get();
	if(m>1) {
		for(int i=1;i<=n;i++) {
			nxt[i]=m;
			pre(rt[i],1,m-1,lx,rx);
		}
	}
	pre(rt[n+1],1,n,lx,rx);
	nxt[n+1]=n+1;
	int x,y;
	ll sx,sy;
	ll tx,ty;
	while(q--) {
		x=Get(),y=Get();
		if(y<m) {
			Get_out(sx,sy,x,y);
			Get_out(tx,ty,x,m);
			Insert(rt[x],lx,rx,nxt[x],(tx-1)*m+ty);
			Insert(rt[n+1],lx,rx,nxt[n+1],(sx-1)*m+sy);
			nxt[x]++,nxt[n+1]++;
		} else {
			Get_out(sx,sy,x,y);
			Insert(rt[n+1],lx,rx,nxt[n+1],(sx-1)*m+sy);
			nxt[n+1]++;
		}
		cout<<(sx-1)*m+sy<<"\n";
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章