P5321 [BJOI2019] 送别 题解--zhengjun
由于大家的做法需要大量分类讨论和代码量,这里提供一种不怎么分类的,容易实现的做法。
首先,由于墙体会随时变化,所以直接对墙体本身维护不是很方便。
我们可以牺牲一点常数,对 \((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;
}