NOIP2017 列队

传送门

鸽了一年

测试点1-6:二维数组模拟。

测试点7-10:把所有询问到的行离散化。

测试点11-16:发现所有事件x等于1,那么每次出队,入队只会对第一行和最后一列造成影响。相当于把最后一列压到第一行,这样就相当于维护一个序列,每次找出第x个元素并放在队尾。可以用线段树维护这个序列,每个节点维护的是当前区间长度,l=r时还存了这个人的编号,当出队的时候就找到第y个人,把这段区间长度设为0,再把他放到末尾。

测试点17-20:每次出队事件,只与当前行和最后一列有关。因此,我们可以维护 n + 1 n+1 n+1棵线段树,维护当前区间长度,人的编号。前 n n n棵线段树维护 n n n行前 m − 1 m-1 m1个人,最后一列单独用一棵线段树存。每次有人出队,就把那个人所在的区间长度-1,再查找最后一棵平衡树的第k小(即应该处于该列的人), 把他加到那一行的线段树的末尾,最后把出队的人加入最后一棵线段树末尾。

但是这样开 n + 1 n+1 n+1棵线段树会爆空间。所以采用动态开点,没有访问到的就当它不存在,访问到的时候再创建一个就好了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 300005;
const int MAX = 10000005;


int n, m, q, now, cnt, maxl;
int rt[MAXN], ls[MAX], rs[MAX], len[MAX], pos[MAXN];
LL val[MAX], Ans;

namespace tree{
	#define mid ((l + r) >> 1)
	inline int calc(int l, int r){
		if(now == n + 1){
			if(r <= n) return r - l + 1;
			if(l <= n) return n - l + 1;
		}
		else{
			if(r <= m - 1) return r - l + 1;
			if(l <= m - 1) return m - l; //(m - 1) - l + 1
		}
		return 0;
	}
	
	inline LL query(int &u, int l, int r, int x){
		if(!u){
			u = ++cnt; len[u] = calc(l, r);
			if(l == r){
				if(now <= n) val[u] = 1ll * (now - 1) * m + l;
				else val[u] = 1ll * m * l;
			}
		}
		len[u]--;
		if(l == r) return val[u];
		if(x <= len[ls[u]]) return query(ls[u], l, mid, x);
		if(!ls[u] && x <= (mid - l + 1)) return query(ls[u], l, mid, x);
		else{
			if(!ls[u]) x -= mid - l + 1;
			else x -= len[ls[u]];
			return query(rs[u], mid + 1, r, x);
		}
	}
	
	inline void insert(int &u, int l, int r, int x, LL k){
		if(!u){
			u = ++cnt; len[u] = calc(l, r);
			if(l == r) val[u] = k;
		}
		len[u]++;
		if(l == r) return;
		if(x <= mid) insert(ls[u], l, mid, x, k);
		else insert(rs[u], mid + 1, r, x, k);
	}
	#undef mid
}


inline int read(){
	int k = 0; char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9') k = k*10 + ch - '0', ch = getchar();
	return k;
}

inline void print(LL x){
	if(x >= 10) print(x / 10);
	putchar(x % 10 + '0');
}

int main(){
	freopen("in.txt", "r", stdin);
	n = read(), m = read(), q = read();
	maxl = max(n, m) + q;
	register int x, y;
	for(int i = 1; i <= q; i++){
		x = read(), y = read();
		if(y == m){
			now = n + 1;
			Ans = tree::query(rt[now], 1, maxl, x);
		}
		else{
			now = x;
			Ans = tree::query(rt[now], 1, maxl, y);
		}
		print(Ans); putchar('\n');
		now = n + 1; pos[now]++;
		tree::insert(rt[now], 1, maxl, n + pos[now], Ans);
		if(y != m){
			Ans = tree::query(rt[now], 1, maxl, x);
			now = x; pos[now]++;
			tree::insert(rt[now], 1, maxl, m - 1 + pos[now], Ans);
		}
	}
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值