P5321 [BJOI2019] 送别 题解--zhengjun

发布时间 2024-01-13 21:35:40作者: A_zjzj

由于大家的做法需要大量分类讨论和代码量,这里提供一种不怎么分类的,容易实现的做法。

首先,由于墙体会随时变化,所以直接对墙体本身维护不是很方便。

我们可以牺牲一点常数,对 \((i,j)\) 建立四个点 \(UL_{i,j},UR_{i,j},DL_{i,j},DR_{i,j}\) 分别表示 \((i-\varepsilon ,j-\varepsilon ),(i-\varepsilon ,j+\varepsilon ),(i+\varepsilon ,j-\varepsilon ),(i+\varepsilon ,j+\varepsilon )\)

这样,在一个状态中,所有点的后继点都可以计算出来。

具体地:

  • \((i,j)-(i+1,j)\) 存在,则 \(DL_{i,j}\to UL_{i+1,j}\)
  • 否则 \(DL_{i,j}\to DR_{i,j}\)

其余的 \(DR_{i,j},UL_{i,j},UR_{i,j}\) 的后继点类似求出来。

这样,整个图就被抽象成了 \(4(n+1)(m+1)\) 个点的若干置换环了。

考虑修改会对置换环造成什么影响:实际上仅仅交换了两个点的后继点(手玩一下就能发现)。

那么,用 FHQ-Treap 维护这些置换环,每次只需要判断一下交换后继点的两个点是否在同一个置换环中:

  • 如果是 \(A\to u\to B\to v\to C(\to A)\),那么改成 \(A\to u\to C(\to A),B\to v(\to B)\)
  • 如果是 \(A\to u\to B(\to A),C\to v\to D(\to C)\),那么改成 \(A\to u\to D\to C\to v \to B(\to A)\)

至于维护距离的话,只需要记录一下每个点到后继点的距离是 \(0/1\) 就行了(FHQ-Treap 多维护一个值就行了)。

代码好写多了,没多少细节,速度也还可以,最大点用时 \(<0.5s\)

本人 30min 码完过了编译直接过了样例,交上去就过了,值得纪念……

代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
#define all(a) (a).begin(),(a).end()
#ifdef DEBUG
template<class T>
ostream& operator << (ostream &out,vector<T> a){
	out<<'[';
	for(T x:a)out<<x<<',';
	return out<<']';
}
template<class T>
vector<T> ary(T *a,int l,int r){
	return vector<T>{a+l,a+1+r};
}
template<class T>
void debug(T x){
	cerr<<x<<endl;
}
template<class T,class...S>
void debug(T x,S...y){
	cerr<<x<<' ',debug(y...);
}
#else
#define debug(...) void()
#endif
const int N=505,M=N*N*4;
int n,m,q;
int k,UL[N][N],UR[N][N],DL[N][N],DR[N][N];
struct node{
	int fa,ls,rs,rnd,siz,val,sum;
}t[M];
mt19937 rnd(181766);
void pushup(int rt){
	t[rt].siz=t[t[rt].ls].siz+t[t[rt].rs].siz+1;
	if(t[rt].ls)t[t[rt].ls].fa=rt;
	if(t[rt].rs)t[t[rt].rs].fa=rt;
	t[rt].fa=0;
	t[rt].sum=t[t[rt].ls].sum+t[t[rt].rs].sum+t[rt].val;
}
void split(int rt,int k,int &x,int &y){
	if(!rt)return x=y=0,void();
	if(k<=t[t[rt].ls].siz)y=rt,split(t[rt].ls,k,x,t[rt].ls);
	else x=rt,split(t[rt].rs,k-t[t[rt].ls].siz-1,t[rt].rs,y);
	pushup(rt);
}
int merge(int x,int y){
	if(!x||!y)return x|y;
	if(t[x].rnd<t[y].rnd){
		t[x].rs=merge(t[x].rs,y);
		return pushup(x),x;
	}else{
		t[y].ls=merge(x,t[y].ls);
		return pushup(y),y;
	}
}
bool which(int rt){
	return t[t[rt].fa].rs==rt;
}
pair<int,int> query(int rt){
	int rk=t[t[rt].ls].siz+1;
	for(;t[rt].fa;rt=t[rt].fa){
		if(which(rt))rk+=t[t[t[rt].fa].ls].siz+1;
	}
	return {rt,rk};
}
void build(){
	static int nex[M],len[M],vis[M];
	for(int i=0;i<=n;i++){
		for(int j=0;j<=m;j++){
			nex[UL[i][j]]=DL[i][j];
			nex[DL[i][j]]=DR[i][j];
			nex[DR[i][j]]=UR[i][j];
			nex[UR[i][j]]=UL[i][j];
		}
	}
	auto link1=[&](int x,int y){
		t[DR[x][y]].val=t[DR[x][y]].sum=1;
		t[UL[x][y+1]].val=t[UL[x][y+1]].sum=1;
		swap(nex[DR[x][y]],nex[UL[x][y+1]]);
	};
	auto link2=[&](int x,int y){
		t[DL[x][y]].val=t[DL[x][y]].sum=1;
		t[UR[x+1][y]].val=t[UR[x+1][y]].sum=1;
		swap(nex[DL[x][y]],nex[UR[x+1][y]]);
	};
	for(int i=0;i<n;i++)link2(i,0),link2(i,m);
	for(int j=0;j<m;j++)link1(0,j),link1(n,j);
	for(int i=1,x;i<=n;i++){
		for(int j=1;j<m;j++){
			scanf("%d",&x);
			if(x)link2(i-1,j);
		}
	}
	for(int i=1,x;i<n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",&x);
			if(x)link1(i,j-1);
		}
	}
	for(int i=1;i<=k;i++)if(!vis[i]){
		int rt=i;
		vis[i]=1;
		for(int j=nex[i];j^i;j=nex[j]){
			vis[j]=1,rt=merge(rt,j);
		}
	}
}
void update(int rt,int op){
	for(;t[rt].rs;rt=t[rt].rs);
	t[rt].val=op;
	for(int fa;fa=t[rt].fa,pushup(rt),fa;rt=fa);
}
void change(int x1,int y1,int x2,int y2,int op){
	if(x1>x2||(x1==x2&&y1>y2))swap(x1,x2),swap(y1,y2);
	static int u,v;
	if(y2==y1+1)u=DR[x1][y1],v=UL[x2][y2];
	else u=DL[x1][y1],v=UR[x2][y2];
	auto [r1,k1]=query(u);
	auto [r2,k2]=query(v);
	if(r1==r2){
		if(k1>k2)swap(k1,k2);
		static int r3,r4,r5;
		split(r1,k2,r4,r5),split(r4,k1,r3,r4);
		update(r3,op),update(r4,op),merge(r3,r5);
	}else{
		static int r3,r4,r5,r6;
		split(r1,k1,r3,r4),split(r2,k2,r5,r6);
		update(r3,op),update(r5,op);
		merge(merge(r3,r6),merge(r5,r4));
	}
}
int getsum(int rt){
	int sum=t[rt].val+t[t[rt].rs].sum;
	for(;t[rt].fa;rt=t[rt].fa){
		if(!which(rt))sum+=t[t[t[rt].fa].rs].sum+t[t[rt].fa].val;
	}
	return sum;
}
int calc(int u,int v){
	auto [r1,k1]=query(u);
	auto [r2,k2]=query(v);
	if(r1!=r2)return -1;
	int res=getsum(u)-getsum(v);
	if(k1>k2)res+=t[r1].sum;
	return res;
}
int find(int x1,int y1,int x2,int y2,int d){
	if(x1==x2){
		if(y1>y2)swap(y1,y2);
		return d?DR[x1][y1]:UL[x2][y2];
	}else{
		if(x1>x2)swap(x1,x2);
		return d?UR[x2][y2]:DL[x1][y1];
	}
}
int main(){
	freopen(".in","r",stdin);
	// freopen(".out","w",stdout);
	scanf("%d%d%d",&n,&m,&q);
	for(int i=0;i<=n;i++){
		for(int j=0;j<=m;j++){
			UL[i][j]=++k,t[k]={0,0,0,(int)rnd(),1,0,0};
			UR[i][j]=++k,t[k]={0,0,0,(int)rnd(),1,0,0};
			DL[i][j]=++k,t[k]={0,0,0,(int)rnd(),1,0,0};
			DR[i][j]=++k,t[k]={0,0,0,(int)rnd(),1,0,0};
		}
	}
	build();
	for(int op,x1,y1,x2,y2,x3,y3,x4,y4,d1,d2;q--;){
		scanf("%d%d%d%d%d",&op,&x1,&y1,&x2,&y2);
		if(op<=2)change(x1,y1,x2,y2,op==1);
		else{
			scanf("%d%d%d%d%d%d",&d1,&x3,&y3,&x4,&y4,&d2);
			printf("%d\n",calc(find(x1,y1,x2,y2,d1),find(x3,y3,x4,y4,d2)));
		}
	}
	return 0;
}