【noi.ac #1771】A. ball

题目

Description
给出 n 条平行的纵向轨道 ,有 m 根横向的短棒支在一些相邻轨道上。如果在某个轨道顶端释放一个小球,它会沿着这个轨道一直下落,一旦碰到短棒就会沿着短棒滚到相邻轨道并继续下落。

为了增加难度,每次可能会拿走某些短棒,或者询问一个从 x 号轨道下落的小球最终落到哪个轨道。

Input
第一行读入两个整数 n,m (2≤n≤5×105,0≤m≤5×105),分别表示轨道的数量和短棒的数量。 第二行读入 m 个整数,第 i 个整数记为 idi (1≤idi<n) ,表示从上到下第 i 根短棒位于编号为 idi 和 idi+1 的轨道之间。 第三行读入一个整数 Q (1≤Q≤5×105),表示操作总数。 接下来读入 Q 行,每行读入两个整数 opt,x。如果 opt=1,表示拿走从上到下的第 x (1≤x≤m) 根短棒。保证不会拿走重复的木棒。如果 opt=2,询问一个初始从轨道 x (1≤x≤n) 下落的小球最终落到了哪个轨道。

Output
对于每一个 opt=2 的操作,输出最终到达的轨道编号。

Sample Input
6 5
2 1 5 3 2
10
2 1
2 2
2 6
1 3
2 6
2 3
1 1
2 2
2 3
2 4
Sample Output
3
4
5
6
1
1
4
2
Hint
一共有 10 组测试数据,数据有梯度。

测试点编号 特殊性质
1,2,3 m,Q≤1000
4,5 没有拿走短棒的操作
6,7 n,m,Q≤50000
8 n=m=Q=100000,初始短棒位置、拿走的短棒、询问的轨道都在其可行范围内均匀随机
9,10 无特殊约定

思路

测试点 1,2,3

每次询问时从高到低枚举每一根短棒,如果在一侧就变成另一侧。 效率: O ( m Q ) O(mQ) O(mQ) 。注意 n n n可能会很大。

测试点 4,5

因为没有修改,可以考虑提前算出所有的答案。 一开始设 a n s i = i ans_i=i ansi=i,从低到高观察每一根木棒:如果它位于 x . . x + 1 x..x+1 x..x+1 之间,相当于交换 a n s x ans_x ansx a n s y ans_y ansy 的值。预处理完后就可以直接输出答案了。

效率 O ( m + Q ) O(m+Q) O(m+Q)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=4e6+77;
int s[N][2],f[N],a[N],yjy[N],n,m,cnt;
void rotate(int x){
	int y=f[x],z=f[y],w=s[y][0]==x;
	if (s[y][w^1]=s[x][w]) f[s[x][w]]=y;
	if (z) s[z][s[z][1]==y]=x;
	s[x][w]=y;f[y]=x;f[x]=z;
}
void splay(int x){
	while (f[x]){
		int y=f[x],z=f[y];
		if (z)
			rotate(((s[z][1]==y)^(s[y][1]==x))?x:y);
		rotate(x);
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
		scanf("%d",&a[i]);
	cnt=1;
	for (int i=m;i;i--){
		++cnt;
		if (f[cnt]=yjy[a[i]])
			s[yjy[a[i]]][1]=cnt;
		yjy[a[i]]=cnt^1;
	
		++cnt;
		if (f[cnt]=yjy[a[i]+1])
			s[yjy[a[i]+1]][1]=cnt;
		yjy[a[i]+1]=cnt^1;
	}
	for (int i=1;i<=n;i++){
		++cnt;
		if (f[cnt]=yjy[i])
			s[yjy[i]][1]=cnt;
	}
	int Q;
	scanf("%d",&Q);
	while (Q--){
		int T,x;
		scanf("%d%d",&T,&x);
		if (T==1){
			int p=(m-x+1)*2,q=p+1;
			splay(p);splay(q);
			if (s[p][1]) f[s[p][1]]=q;
			if (s[q][1]) f[s[q][1]]=p;
			swap(s[p][1],s[q][1]);
		}
		else {
			x+=2*m+1;
			splay(x);
			int ret=x;
			for (;s[ret][0];ret=s[ret][0]);
			printf("%d\n",ret<=2*m+1?a[m-ret/2+1]+(ret&1):x-2*m-1);
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值