P9358 题解

不难发现,最开始有 \(n\) 条链,并且由于每个点最多有一个桥,所以我们的交换操作实际上等价于将相邻的两条链断开,然后将它们后半部分交换。并且每个点在路径中的相对位置不变。

于是考虑维护这些链。

有一个很直观的思路就是维护点对 \((i,j)\) 表示最开始第 \(i\) 条链的第 \(j\) 个点在哪条链中,我们需要快速改变它以及它后面点的引索,所以考虑把在一条链中的点对放到一棵以 \(j\) 为键值的 FHQtreap 上并维护每个点所属的 treap 然后操作就变成了分裂之后交换子树以及查询最大值,这个很好维护,可问题是点数是 \(O(nm)\) 级别的。

所以考虑维护三元组表示最开始第 \(i\) 条链上第 \(l\) 到第 \(r\) 个点当前所在的链,不难发现最开始有 \(n\) 个三元组且一次操作最多使一个三元组分裂为 \((x,l,t)\)\((x,t+1,r)\) 故三元组数量为 \(O(n + m)\) 级别。

为了快速检索每个点在那个三元组中,把 \(x\) 相同的三元组放在一个可以支持快速插入删除寻找前驱后继的平衡树中,在平衡树中储存三元组所在的节点的编号即可。

如此我们便在 \(O(n \log n)\) 的时间内解决了问题。

#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> 
#include <ext/pb_ds/tree_policy.hpp>
using namespace std;
const int maxn = 5e5+114;
__gnu_pbds::tree< pair<int, int>, __gnu_pbds::null_type, less< pair<int, int> >,__gnu_pbds::rb_tree_tag,__gnu_pbds::tree_order_statistics_node_update > Index[maxn];
//(r,tot)
struct Node{
	int l,r,p;
	int fa;
	int ls,rs;
	int val;
}tr[maxn];
int root[maxn];
map<int,int> found;
int tot;
int clone(int l,int r,int p){
	tr[++tot].l=l,tr[tot].r=r,tr[tot].p=p,tr[tot].val=rand();
	return tot;
}
int merge(int x,int y){
	if(!x||!y) return x+y;
	if(tr[x].val>tr[y].val){
		tr[x].rs=merge(tr[x].rs,y);
		tr[tr[x].rs].fa=x;
		return x;
	}
	else{
		tr[y].ls=merge(x,tr[y].ls);
		tr[tr[y].ls].fa=y;
		return y;
	}
}
void split(int cur,int x,int &l,int &r){
	if(cur==0){
		l=r=0;
		return ;
	}
	if(tr[cur].l>x){
		r=cur;
		split(tr[cur].ls,x,l,tr[cur].ls);
		tr[tr[cur].ls].fa=cur;
	}
	else{
		l=cur;
		split(tr[cur].rs,x,tr[cur].rs,r);
		tr[tr[cur].rs].fa=cur;
	}
}
int find_root(int x){//寻找点 x 所在的根 
	while(tr[x].fa!=0){
		x=tr[x].fa;
	}
	return x;
}
int Ord_split(int x,int pos){//将 (x,pos) 所在的有序段分裂为 [l,x] 与 [x+1,r] 
	int y = (*Index[pos].lower_bound(make_pair(x,x))).second;
	int lt=tr[y].l,rt=tr[y].r,p=tr[y].p;
	y=find_root(y);
	if(x==rt) return y;
	int pos_rt=found[y];
	int tr1=0,tr2=0,tr3=0;
	split(y,lt-1,tr1,tr2);
	split(tr2,lt,tr2,tr3);
	int New1=clone(lt,x,p),New2=clone(x+1,rt,p);
	found[y]=0;
	y=merge(tr1,merge(New1,merge(New2,tr3)));
	tr[y].fa=0,found[y]=pos_rt,root[pos_rt]=y;
	Index[pos].erase(Index[pos].lower_bound(make_pair(x,x)));
	Index[pos].insert(make_pair(x,New1));
	Index[pos].insert(make_pair(rt,New2));	
	return y;
} 
int n,m,q;
void build(){
	for(int i=1;i<=n;i++){
		int y=clone(1,m+1,i);
		tr[y].fa=0,found[y]=i,root[i]=y;
		Index[i].insert(make_pair(m+1,y));
	}
}
int End(int x){
	if(tr[x].rs==0) return tr[x].p;
	else return End(tr[x].rs);
}
int query(int x){//查询从点 x 开始最后会到那个点 
	return End(root[x]);
}
void opt(int a,int b){
	int x=Ord_split(b,a);
	int y=Ord_split(b,a+1);
	x=find_root(x);
	y=find_root(y);
	int x_pos = found[x];
	int y_pos = found[y];
	int tr1=0,tr2=0,tr3=0,tr4=0;
	split(x,b,tr1,tr2);
	split(y,b,tr3,tr4);
	found[x]=0;
	found[y]=0;
	x=merge(tr1,tr4);
	y=merge(tr3,tr2);
	tr[x].fa=0,found[x]=x_pos,root[x_pos]=x;
	tr[y].fa=0,found[y]=y_pos,root[y_pos]=y;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>q;
build();
for(int i=1;i<=q;i++){
	int op;
	cin>>op;
	if(op==1){
		int a,b;
		cin>>a>>b;
		opt(a,b);
	}
	else{
		int a;
		cin>>a;
		cout<<query(a)<<'\n';
	}
}
return 0;
}
posted @ 2024-01-30 23:51  ChiFAN鸭  阅读(8)  评论(0编辑  收藏  举报