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 想要計算每一次離隊事件中,離隊的同學 的編號是多少。
注意:每一個同學的編號不會隨著離隊事件的發生而改變,在發生離隊事件後 方陣中同學的編號可能是亂序的。
題解:
感覺這道題是考場上沒做的題目中最簡單的了……考慮的暴力演算法,用個線段樹,長度開到,前位一開始的值為,然後每次就線上段樹上二分,找到第個,把它的值變為0,然後在後面再加上一個就行了,對於滿分演算法,只需要用棵線段樹,分別維護行和最後一列就可以了,然後動態開點。
程式碼:
#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]);
}
}
}