首页 > 其他分享 >P5321 [BJOI2019] 送别 题解--zhengjun

P5321 [BJOI2019] 送别 题解--zhengjun

时间:2024-01-13 21:45:37浏览次数:33  
标签:BJOI2019 rt x1 P5321 int 题解 x2 y1 y2

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

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

我们可以牺牲一点常数,对 \((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;
}

标签:BJOI2019,rt,x1,P5321,int,题解,x2,y1,y2
From: https://www.cnblogs.com/A-zjzj/p/17962995

相关文章

  • AT_arc125_c [ARC125C] LIS to Original Sequence 题解
    题目传送门前置知识贪心|构造解法对于任意一个未加入序列\(P\)的数\(x<A_{i}(1\lei\lek-1)\),如果其放在了\(A_{i}\)的前面,会导致最长上升子序列长度加一,从而不符合题目要求。因此我们需要把\(x\)放在\(A_{i}\)后面,同理,为符合题目要求,我们仅选择放最小的那一个......
  • P2198 杀蚂蚁 题解
    题目大意有一条长度为\(n\)个单位长度的路,蚂蚁们要从起点走到终点。蚂蚁们每走\(1\)个单位距离需要\(T\)秒钟。现在,出题人可以在路上修筑\(3\)种防御塔来阻挡蚂蚁的进攻,每个单位距离只能修筑\(1\)座塔,塔的作用分别如下:激光塔:蚂蚁在塔前时每秒会受到\(r\)点伤害。......
  • P3243 [HNOI2015] 菜肴制作 题解
    前言今天考试考到这道题,挂惨了。题意有\(n\)道菜肴,编号为\(1\simn\)。有\(m\)个条件,形如\((i,j)\),表示菜肴\(i\)必须在菜肴\(j\)之前制作。需求出一个菜肴的制作顺序,满足:在满足所有限制的前提下,\(1\)号菜肴尽量优先制作。在满足所有限制,\(1\)号菜肴尽量优......
  • P9007 [入门赛 #9] 最澄澈的空与海 (Hard Version) 题解
    Updon2023.10.1408:21:修改了推式子和题意的一些小错误。前言一道恐怖的绿题。显然我认为应该是蓝题。(不过在这篇题解写到一半的时候升蓝了,感谢@StudyingFather。)名字挺好的。题意给定\(n\),求出满足以下条件的三元组\((x,y,z)\)的组数:\(x\ge0,z\ge1\)。\(......
  • AT_abc243_g [ABC243G] Sqrt题解
    题目大意有一个数列,初始时只有一个数\(X\)。你可以对它进行一种操作:设末尾的数为\(Y\),从\(1\sim\sqrt{Y}\)中选一个数加到数列的末尾。如此进行\(10^{100}\)次操作,问数列一共有多少种可能的状态。解法考虑DP。设\(dp_i\)表示以数字\(i\)开头的数列可能的状态。设......
  • CF713D 题解
    题意给一个\(01\)矩阵,多次求在给定区间内最大的全\(1\)正方形边长。思路容易想到二分:先预处理出以每个位置为右下角的最大合法正方形的边长\(mx_{i,j}\),然后对于每个询问,我们二分边长\(mid\),设当前询问的区间左上角为\((x_1,y_1)\),右下角为\((x_2,y_2)\),则能取到\(mi......
  • AT_arc167_e 题解
    题意给定\(k\)和一个排列\(P'\),问有多少个排列\(P\)以最少步数交换相邻两个元素来进行收敛,最终的排列可能是\(P'\),一个排列是收敛的当且仅当对于每一个数,在该数前且比这个数大的数的个数不超过\(k\)个。思路考虑正向的让一个排列收敛,我们设在第\(i\)个位置前且比\(P......
  • AT_agc054_c 题解
    题意给定\(k\)和一个排列\(P'\),问有多少个排列\(P\)以最少步数交换相邻两个元素来进行收敛,最终的排列可能是\(P'\),一个排列是收敛的当且仅当对于每一个数,在该数前且比这个数大的数的个数不超过\(k\)个。思路考虑正向的让一个排列收敛,我们设在第\(i\)个位置前且比\(P......
  • P9754 题解
    题意不难理解,不多赘述。思路首先考虑对于性质A的情况,我们可以这样做:定义一个代表变量的结构体,里面存几个参数:首先肯定要存种类(\(type\))和名称(\(name\)),其次为了方便,我们把该变量的大小(\(siz\)),起始位置(\(fir\))和对齐要求(\(mx\))也存了。操作二\(type\),\(name\),\(siz\)和\(m......
  • AT_cf17_final_j 题解
    题意给定一棵既有点权也有边权的树,构造一个完全图,图中两点间边的边权为树中两点点权之和加上两点间的距离,求该图的最小生成树。思路发现完全图总边数太大,考虑减少边数。这里有一个性质:如果在一个图中选取任意个联通的边集,使得它们的并为全集,则整个图的最小生成树中的边一定在......