[NOIP2017]列队(树状数组)

定义第i行为所有的点(i,j),0<j<m

可以发现,每一行是相对独立的,每一次操作只会影响到当前行和最后一列

考虑每一行和最后一列各开一个树状数组,但这样显然会爆空间

实际上,对于没有离队过的点是没必要储存的,可以直接算出编号,

因此只要用vector储存每一行和最后一列后加入的点即可

还需要预处理一个数组d[i]表示第i次询问当前行的离队的点的纵坐标

这个可以离线做出来,然后只需要对最后一列维护一个树状数组即可

Code

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#define ll long long
#define lowbit(x) ((x)&(-x))
#define N 300010
using namespace std;

struct info{int p,id;}A[N];
int n,m,qn,mx,d[N],a[N<<1],q[N][2];
vector<info> t[N];
vector<ll>v[N],lst;
inline 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*10+ch-'0';ch=getchar();}
	return x*f;
}
void add(int x,int y){for(;x<=mx;x+=lowbit(x))a[x]+=y;}
int Q(int x){
	int r=0;for(;x;x-=lowbit(x))r+=a[x];
	return r;
}

int Find(int R){
	int tmp=0;
	for(int l=0,r=mx;l<=r;){
		int mid=(l+r)>>1;
		if(Q(mid)>=R) tmp=mid,r=mid-1;
		else l=mid+1;
	}
	return tmp;
}

void Init(){
	n=read(),m=read(),qn=read(),mx=max(n,m)+qn;
	for(int i=1;i<=qn;++i){
		q[i][0]=read(),q[i][1]=read();
		if(q[i][1]!=m) t[q[i][0]].push_back((info){q[i][1],i});
	}
	for(int i=1;i<=mx;++i) add(i,1);
	for(int i=1;i<=n;++i){
		for(info x:t[i]) add(d[x.id]=Find(x.p),-1);
		for(info x:t[i]) add(d[x.id],1);
	}
}

void solve(){
	ll Ans;
	for(int i=1;i<=qn;++i){
		int x=Find(q[i][0]);
		Ans=(x<=n)?(ll)x*m:lst[x-n-1];
		add(x,-1);
		if(q[i][1]!=m){
			v[q[i][0]].push_back(Ans);
			Ans=(d[i]<m)?(q[i][0]-1)*1ll*m+d[i]:v[q[i][0]][d[i]-m];
		}
		lst.push_back(Ans);
		printf("%lld\n",Ans);
	}
}

int main(){Init();solve();}

 

posted @ 2018-07-21 09:32  void_f  阅读(1192)  评论(1编辑  收藏  举报