1. 程式人生 > >NOIP2017D2T3列隊 動態開點線段樹

NOIP2017D2T3列隊 動態開點線段樹

題目描述
Sylvia 是一個熱愛學習的女♂孩子。

前段時間,Sylvia 參加了學校的軍訓。眾所周知,軍訓的時候需要站方陣。

Sylvia 所在的方陣中有n \times mn×m 名學生,方陣的行數為 nn ,列數為 mm 。

為了便於管理,教官在訓練開始時,按照從前到後,從左到右的順序給方陣中 的學生從 1 到 n \times mn×m 編上了號碼(參見後面的樣例)。即:初始時,第 ii 行第 jj 列 的學生的編號是(i-1)\times m + j(i−1)×m+j 。

然而在練習方陣的時候,經常會有學生因為各種各樣的事情需要離隊。在一天 中,一共發生了 q q 件這樣的離隊事件。每一次離隊事件可以用數對(x,y) (1 \le x \le n, 1 \le y \le m)(x,y)(1≤x≤n,1≤y≤m) 描述,表示第 xx 行第 yy 列的學生離隊。

在有學生離隊後,隊伍中出現了一個空位。為了隊伍的整齊,教官會依次下達 這樣的兩條指令:

向左看齊。這時第一列保持不動,所有學生向左填補空缺。不難發現在這條 指令之後,空位在第 xx 行第 mm 列。

向前看齊。這時第一行保持不動,所有學生向前填補空缺。不難發現在這條 指令之後,空位在第 nn 行第 mm 列。
教官規定不能有兩個或更多學生同時離隊。即在前一個離隊的學生歸隊之後, 下一個學生才能離隊。因此在每一個離隊的學生要歸隊時,隊伍中有且僅有第 nn 行 第 mm 列一個空位,這時這個學生會自然地填補到這個位置。

因為站方陣真的很無聊,所以 Sylvia 想要計算每一次離隊事件中,離隊的同學 的編號是多少。

注意:每一個同學的編號不會隨著離隊事件的發生而改變,在發生離隊事件後 方陣中同學的編號可能是亂序的。

題解:

感覺這道題是考場上沒做的題目中最簡單的了……考慮n=1的暴力演算法,用個線段樹,長度開到m+c,前m位一開始的值為1,然後每次就線上段樹上二分,找到第y1,把它的值變為0,然後在後面再加上一個1就行了,對於滿分演算法,只需要用n+1棵線段樹,分別維護n行和最後一列就可以了,然後動態開點。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=600010; const int inf=2147483647; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); return x*f; } LL num[Maxn*40]; int n,m,q,root[Maxn],lc[Maxn*40],rc[Maxn*40],c[Maxn*40],cnt,end[Maxn]; LL p(LL x,LL y){return (LL)(m)*(LL)(x-1)+(LL)(y);} void build(int &u,int l,int r,int ll,int rr) { if(!u)u=++cnt;c[u]=rr-ll+1; if(l==ll&&r==rr)return; int mid=l+r>>1; if(rr<=mid)build(lc[u],l,mid,ll,rr); else if(ll>mid)build(rc[u],mid+1,r,ll,rr); else build(lc[u],l,mid,ll,mid),build(rc[u],mid+1,r,mid+1,rr); } int type;//0最後一列 第type行 LL del(int &u,int l,int r,int k) { if(!u)u=++cnt,c[u]=r-l+1; c[u]--; if(l==r) { if(num[u])return num[u]; if(!type)return p(l,m); else return p(type,l); } int mid=l+r>>1,ll; if(!lc[u])ll=mid-l+1; else ll=c[lc[u]]; if(k<=ll)return del(lc[u],l,mid,k); else return del(rc[u],mid+1,r,k-ll); } LL tnum; void ins(int &u,int l,int r,int p) { if(!u)u=++cnt;c[u]++; if(l==r){num[u]=tnum;return;} int mid=l+r>>1; if(p<=mid)ins(lc[u],l,mid,p); else ins(rc[u],mid+1,r,p); } int main() { n=read(),m=read(),q=read();cnt=0; for(int i=1;i<=n;i++) { build(root[i],1,600000,1,m-1); end[i]=m-1; } build(root[n+1],1,600000,1,n); end[n+1]=n; while(q--) { int x=read(),y=read(); if(y==m) { type=0; LL t=del(root[n+1],1,600000,x); printf("%lld\n",t); end[n+1]++; tnum=t; ins(root[n+1],1,600000,end[n+1]); } else { type=x; LL t1=del(root[x],1,600000,y); printf("%lld\n",t1); type=0; LL t2=del(root[n+1],1,600000,x); end[n+1]++; tnum=t1; ins(root[n+1],1,600000,end[n+1]); end[x]++; tnum=t2; ins(root[x],1,600000,end[x]); } } }