调了一个下午+晚上了
查看原帖
调了一个下午+晚上了
35754
whiteqwq楼主2020/10/11 20:47

真的调不出来了,始终WA 70\text{WA\ 70},应该是维护ansans时错了(不确定)。

思路就是根号分治,看的第二篇题解,写了注释方便食用。

#include<stdio.h>
#include<math.h>
#include<vector>
#include<string.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=100005,maxt=505;
int n,m,lastans,lim,tot;
int a[maxn],val[maxn],size[maxn],id[maxn],ans[maxt][maxn];//原序列,某个值实际是多少,它是第几个size大于lim的,某个值与其他值的最近距离
vector<int>v[maxn];//附属集合
void build(int x){//暴力求ans
	int dis;
	if(id[x]==0)
		id[x]=++tot;
	memset(ans[id[x]],0x3f,sizeof(ans[id[x]]));
	v[x].clear();
	dis=inf;
	for(int i=1;i<=n;i++){
		if(a[i]==x)
			dis=0;
		else dis++;
		ans[id[x]][a[i]]=min(ans[id[x]][a[i]],dis);
	}
	dis=inf;
	for(int i=n;i>=1;i--){
		if(a[i]==x)
			dis=0;
		else dis++;
		ans[id[x]][a[i]]=min(ans[id[x]][a[i]],dis);
	}
}
void init(){//预处理
	lim=sqrt(n);
	for(int i=1;i<=n;i++)
		size[a[i]]++,v[a[i]].push_back(i);
	for(int i=0;i<=n;i++){
		val[i]=i;
		if(size[i]>lim)
			build(i);
	}
}
void merge(int x,int y){//合并两个附属集合
	vector<int>res;
	for(int i=0,j=0;i<v[x].size()||j<v[y].size();){
		if(j>=v[y].size()||(i<v[x].size()&&v[x][i]<v[y][j]))
			res.push_back(v[x][i]),i++;
		else res.push_back(v[y][j]),j++;
	}
	v[y]=res;
}
void updateA(int x,int y){//对于v比较少的直接合并
	for(int i=0;i<v[x].size();i++)
		a[v[x][i]]=y;
	for(int i=1;i<=tot;i++)
		ans[i][y]=min(ans[i][y],ans[i][x]);
	merge(x,y);
}
void updateB(int x,int y){//对于v比较大的暴力重构
	for(int i=1;i<=n;i++)
		if(a[i]==x)
			a[i]=y;
	build(y);
}
void update1(int x,int y){//size[x]和size[y]都小于等于lim
	if(size[x]+size[y]<=lim)
		updateA(x,y);
	else updateB(x,y);
}
void update2(int x,int y){//size[x]小于等于lim,size[y]大于lim
	if(size[x]+v[y].size()<=lim)
		updateA(x,y);
	else updateB(x,y);
}
void update3(int x,int y){//size[x]和size[y]都大于lim
	updateB(x,y);
}
void update(int x,int y){//修改操作
	if(size[val[x]]==0||val[x]==val[y])
		return ;
	int px=val[x],py=val[y];
	if(size[val[x]]>size[val[y]])
		val[y]=val[x],swap(px,py);
	val[x]=n+1,x=px,y=py;
	if(x==n+1||y==n+1)
		return ;
	if(size[x]<=lim&&size[y]<=lim)
		update1(x,y);
	if(size[x]<=lim&&size[y]>lim)
		update2(x,y);
	if(size[x]>lim&&size[y]>lim)
		update3(x,y);
	size[y]+=size[x],size[x]=0;
	v[x].clear();
}
int calc(int x,int y){//对于两个附属集合求解
	int i=0,j=0,res=inf;
	if(size[x]==0||size[y]==0)
		return inf;
	while(i<v[x].size()&&j<v[y].size()){
		if(v[x][i]<v[y][j])
			res=min(res,v[y][j]-v[x][i]),i++;
		else res=min(res,v[x][i]-v[y][j]),j++;
	}
	if(i<v[x].size())
		res=min(res,abs(v[x][i]-v[y][j-1]));
	if(j<v[y].size())
		res=min(res,abs(v[y][j]-v[x][i-1]));
	return res;
}
int query1(int x,int y){//同update1
	return calc(x,y);
}
int query2(int x,int y){//同update2
	return min(ans[id[y]][x],calc(x,y));
}
int query3(int x,int y){//同update3
	return min(min(ans[id[x]][y],ans[id[y]][x]),calc(x,y));
}
int query(int x,int y){//查询操作
	x=val[x],y=val[y];
	if(x==n+1||y==n+1||size[x]==0||size[y]==0)
		return -1;
	if(x==y)
		return 0;
	if(size[x]>size[y])
		swap(x,y);
	if(size[x]<=lim&&size[y]<=lim)
		return query1(x,y);
	if(size[x]<=lim&&size[y]>lim)
		return query2(x,y);
	if(size[x]>lim&&size[y]>lim)
		return query3(x,y);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	init();
	for(int i=1;i<=m;i++){
		int t,x,y;
		scanf("%d%d%d",&t,&x,&y);
		x^=lastans,y^=lastans;
		if(t==1)
			update(x,y);
		if(t==2){
			int res=query(x,y);
			if(res==-1)
				lastans=0,puts("Ikaros");
			else lastans=res,printf("%d\n",res);
		}
	}
	return 0;
}
2020/10/11 20:47
加载中...