「LG5397-天降之物」题解

P5397 [Ynoi2018] 天降之物

sol

基本部分

考虑根号分治。令阈值为 \(B\)

对于每一种数,将其位置信息分为两部分存储:主体部分(\(siz>B\))与附加部分(\(siz\le B\))。

主体部分无需额外维护,附加部分用动态数组存下其所有位置。

令出现次数 \(>B\) 也就是有主体部分的数为大数,其余数为小数。

由于大数个数 \(\le \frac{n}{B}\),因此我们可以存下每一个大数的主体部分对于其余每一个数的答案。

以上是信息维护,下面讲解具体的修改与查询方式。

查询

首先,假如说存在大数,那么就可以利用存储的信息 \(O(1)\) 得到大数的主体部分与另一个数的答案。

那么事实上我们只需要考虑两个数的附加部分的贡献就行,双指针 \(O(B)\) 做到。

单次查询复杂度 \(O(B)\)

修改

我们可以通过简单的技巧交换操作的 \(x,y\),使得 \(x\) 大小小于 \(y\)

首先我们把原序列中所有 \(a=x\) 的位置都赋值为 \(y\)。对于附加部分,是 \(O(B)\) 的。对于主体部分,假如说存在主体部分,那么 \(x,y\) 的大小至少为 \(B\),这样的操作不超过 \(\frac{n}{B}\) 次,因此我们可以考虑每次暴力遍历整个数组修改,复杂度为 \(O(\frac{n^2}{B})\)

然后用所有大数对 \(x\) 的答案更新其对 \(y\) 的答案,至多 \(\frac{n}{B}\) 个大数,直接遍历即可。

\(x\) 为大数或者合并后 \(y\) 的附加部分大小超过 \(B\),那么直接 \(O(n)\) 重构 \(y\) 对其它数的 \(ans\) 信息并清空附加部分。前者出现次数不超过 \(\frac{n}{B}\),对于后者只讨论 \(x\) 为小数的情况,显然出现次数也不超过 \(\frac{n}{B}\)。复杂度为 \(O(\frac{n^2}{B})\)

细节

我们可以通过储存每一个数的实际对应编号以方便交换两数。

\(B\) 可以偏大于 \(\sqrt n\),因为大数复杂度更高一些。

code

const int N=1e5+5,B=500,M=N/B+5;

int n,m;
int a[N];
int mp[N];
vec<int> p[N];
int id[N],idx;
int ans[M][N];

inline void rebuild(int x){
	if(!id[x])id[x]=++idx;
	memset(ans[id[x]],0x3f,sizeof ans[id[x]]);
	ans[id[x]][x]=0;
	int d=inf;
	rep(i,1,n)
		if(a[i]==x)d=0;
		else chmin(ans[id[x]][a[i]],++d);
	d=inf;
	per(i,n,1)
		if(a[i]==x)d=0;
		else chmin(ans[id[x]][a[i]],++d);
	p[x].clear();
}

inline vec<int> merge(vec<int> &a,vec<int> &b){
	vec<int> c(a.size()+b.size());
	merge(a.begin(),a.end(),b.begin(),b.end(),c.begin());
	return c;
}

inline void modify(int &x,int &y){
	if(x==y||!x)return;
	if(!y)return swap(x,y);
	if(id[x])swap(x,y);
	rep(i,1,idx)chmin(ans[i][y],ans[i][x]);
	if(id[x]){
		rep(i,1,n)if(a[i]==x)a[i]=y;
		rebuild(y);
	}else{
		for(auto i:p[x])a[i]=y;
		p[y]=merge(p[x],p[y]);
		if(p[y].size()>B)rebuild(y);
	}
	p[x].clear();x=0;
}

inline int query(int &x,int &y){
	if(!x||!y)return -1;
	if(x==y)return 0;
	int res=inf;
	vec<int> tp=merge(p[x],p[y]);
	repl(i,1,tp.size())if(a[tp[i-1]]^a[tp[i]])chmin(res,tp[i]-tp[i-1]);
	if(id[x])chmin(res,ans[id[x]][y]);
	if(id[y])chmin(res,ans[id[y]][x]);
	return res;
}

int main(){
	read(n),read(m);
	rep(i,1,n)read(a[i]),p[a[i]].pub(i),mp[a[i]]=a[i];
	rep(i,1,n)if(p[a[i]].size()>B)rebuild(a[i]);
	int lst=0;
	rep(i,1,m){
		int o=read(),x=read()^lst,y=read()^lst;
		if(o==1)modify(mp[x],mp[y]);
		else{
			lst=query(mp[x],mp[y]);
			if(~lst)write(lst,'\n');
			else writes("Ikaros\n"),lst=0;
		}
	}
	return 0;
}
posted @ 2025-05-30 23:27  LastKismet  阅读(14)  评论(0)    收藏  举报