【题解】[NOIP2017 提高组] 列队

题意

给定一个 n*m 的队列,每次操作选取一个位置 (i,j) 出队,然后填补空位。输出每次出队的编号。

Solution:

考点:模拟+数据结构。

算法一

对于 n,m<=1000 的数据,直接暴力模拟;

对于 n=1 的数据,可以树状数组维护:

请添加图片描述
稍加修改,可以得到 x=1 的做法,

请添加图片描述
期望得分 60pts

算法二

再来看 n=2 的情况:
请添加图片描述
于是不难得到下述算法:

  1. 建立一个以第 m 行为元素的序列
  2. 对于每次操作,判断是否在当前行内,如果是则先在当前行内删除,再插入列尾;否则在列中查询排名 x 的数,先将它删去,然后插入列尾。如果是第一种情况就要把列中排名 x 的数插到行中去。

比较棘手的是空间会爆。观察到被删除的节点只有 q 个,所以可以 动态开点+线段树二分 ,只有一个 log

可以用 线段树 实现。时间复杂度 O(nlogn)

纯属口胡,不喜勿喷。

#include<bits/stdc++.h>
#define INF 1e9
#define ll long long
#define PII pair<ll,int>
#define All(a) a.begin(),a.end()
#define L t[p].lson
#define R t[p].rson
using namespace std;
const int mx=4e7+5;
inline int read() {
	int x=0,f=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
	while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return x*f;
}
int n,m,q,cnt[mx],tot;
ll res;
struct SegmentTree{
	int lson,rson,siz,add;
	ll val;
}t[mx];
void PushUp(int p) {
	t[p].siz=t[L].siz+t[R].siz;
}
void update(int p,int l,int r,int x,ll y) {
	if(l==r) {
		t[p].val=y; t[p].siz=1;
		return;
	} 
	int mid=l+r>>1;
	if(!t[p].lson) t[p].lson=++tot;
	if(!t[p].rson) t[p].rson=++tot;
	if(t[p].add) {
		t[p].add=0;
		t[L].siz=mid-l+1; t[L].add=1;
		t[R].siz=r-mid; t[R].add=1;
	}
	if(x<=mid) {
		update(L,l,mid,x,y);
	}
	else {
		update(R,mid+1,r,x,y);
	}
	PushUp(p);
}
int query(int p,int l,int r,int x) {
	if(l==r) {
		return l;
	}
	int mid=l+r>>1; 
	if(!t[p].lson) t[p].lson=++tot;
	if(!t[p].rson) t[p].rson=++tot;
	if(t[p].add) {
		t[p].add=0;
		t[L].siz=mid-l+1; t[L].add=1;
		t[R].siz=r-mid; t[R].add=1;
	}
	if(x<=t[L].siz) return query(L,l,mid,x);
	return query(R,mid+1,r,x-t[L].siz); 
}
void Delete(int p,int l,int r,int x) {
	if(l==r) {
		t[p].siz=0; res=t[p].val;
 		return; 
	}
	int mid=l+r>>1;
	if(!t[p].lson) t[p].lson=++tot;
	if(!t[p].rson) t[p].rson=++tot;
	if(t[p].add) {
		t[p].add=0;
		t[L].siz=mid-l+1; t[L].add=1;
		t[R].siz=r-mid; t[R].add=1;
	}
	if(x<=mid) Delete(L,l,mid,x);
	else Delete(R,mid+1,r,x);
	PushUp(p);
}
void build(int p,int l,int r,int ql,int qr) {
	if(ql<=l&&r<=qr) {
		t[p].siz=r-l+1; t[p].add=1; 
		return;
	}
	if(!t[p].lson) t[p].lson=++tot;
	if(!t[p].rson) t[p].rson=++tot;
	int mid=l+r>>1;
	if(ql<=mid) build(L,l,mid,ql,qr);
	if(mid<qr) build(R,mid+1,r,ql,qr);
	PushUp(p);
}
int main() {
	n=read(),m=read(),q=read(); tot=n+1;
	for(int i=1;i<=n;i++) {
		build(i,1,m+q,1,m-1);
		cnt[i]=m-1;
	}
	for(int i=1;i<=n;i++) {
		update(n+1,1,n+q,i,1ll*i*m); 
	}
	for(int i=1;i<=q;i++) {
		int x=read(),y=read();
		if(y<=m-1) {
			int z=query(x,1,m+q,y);
			Delete(x,1,m+q,z); ll tmp;
			if(z<=m) tmp=1ll*(x-1)*m+z;
			else tmp=res;
			printf("%lld\n",tmp);
			z=query(n+1,1,n+q,x);
			Delete(n+1,1,n+q,z);
			update(x,1,m+q,m+i-1,res);
			update(n+1,1,n+q,n+i,tmp);
		} 
		else {
			int z=query(n+1,1,n+q,x);
			Delete(n+1,1,n+q,z);
			printf("%lld\n",res);
			update(n+1,1,n+q,n+i,res);
		}
	}
} 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值