题目
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);
}
}
}