Link
我们知道在没有修改的时候有一个很简单的根号分治算法,现在考虑将其扩展至带修。
设(cnt_x)为(x)的出现次数,(pos_x)为(x)的出现位置,(dis_x)为(x)到其它数的最短距离。
记(B)为分块大小,(S={x|cnt_x>B})。
这里先给出整体的大致思路:
保证任意时刻(|pos_x|le B),这样我们可以用(O(B))的时间进行零散的合并/查询。
如果(|pos_x|>B)那么我们处理(dis_x),很显然处理(dis)的总次数是(O(frac nB))的。
然后我们给出具体做法:
最开始先求出(pos,cnt),对于(xin S),清空(pos_x)并求出(dis_x)。
考虑如何处理修改:
(1.)归并(pos_x,pos_y)并清空(pos_x)。
(2.)如果合并之后(|pos_y|>S),那么清空(pos_y)并更新(dis_y)。
(3.)如果(xin S),那么用(dis_x)更新(dis_y)并清空(dis_x)。
(4.)对所有(win S),用(dis_{w,x})更新(dis_{w,y})。
考虑如何回答询问:
(1.)先特判(ans=0)的情况。
(2.)利用(dis_{x,y},dis_{y,x})更新(ans)。
(3.)遍历(pos_x,pos_y)更新(ans)。
不难发现这样做可以完整地维护与统计(pos,dis)的信息。
总的时间复杂度是(O(nB+frac{n^2}B)),当(B=sqrt n)达到最优(O(n^{1.5}))。
注意到空间复杂度是(O(frac{n^2}B))的,因此可以适当调大(B)来卡空间。
#include<cmath>
#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
#include<unordered_set>
namespace IO
{
char ibuf[(1<<21)+1],*iS,*iT;
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
}
using IO::read;
const int N=100001,B=3,inf=1000000000;
int n,q,cnt=100000,a[N],fa[N*3],num[N*3],id[N*3];
std::vector<int>pos[N],dis[N],t;
std::unordered_set<int>res;
int find(int x){return fa[x]==x? x:fa[x]=find(fa[x]);}
int get(int x){return num[find(a[x])];}
void min(int&a,int b){a=a<b? a:b;}
void init(int i)
{
dis[i].resize(100001,inf);
for(int j=1,p=0;j<=n;++j)
{
while(p+1<(int)pos[i].size()&&j>pos[i][p]&&j-pos[i][p]>pos[i][p+1]-j) ++p;
min(dis[i][get(j)],abs(j-pos[i][p]));
}
pos[i].clear(),res.insert(i);
}
int main()
{
freopen("1.in","r",stdin);freopen("1.out","w",stdout);
n=read(),q=read();
for(int i=1;i<=n;++i) pos[a[i]=read()].push_back(i);
for(int i=1;i<=300000;++i) fa[i]=i;
for(int i=1;i<=100000;++i) num[i]=id[i]=i;
for(int i=0;i<=100000;++i) if(pos[i].size()>=B) init(i);
for(int opt,x,y,ans=0;q;--q)
{
opt=read(),x=read()^ans,y=read()^ans;
if(opt==1)
{
if(x==y) continue;
num[++cnt]=y,fa[find(id[x])]=fa[find(id[y])]=cnt,id[y]=cnt,id[x]=++cnt,num[id[x]]=x;
t.clear(),t.resize(pos[x].size()+pos[y].size()),std::merge(pos[x].begin(),pos[x].end(),pos[y].begin(),pos[y].end(),t.begin());
pos[x].clear(),pos[y]=t,res.erase(x),res.erase(y);
if(dis[x].size()||dis[y].size())
{
if(dis[y].empty()) dis[y].swap(dis[x]);
else if(dis[x].size()) {for(int i=0;i<=100000;++i)min(dis[y][i],dis[x][i]);dis[x].clear();}
res.insert(y);
}
if(pos[y].size()>=B) init(y);
for(int i:res) min(dis[i][y],dis[i][x]),dis[i][x]=inf;
}
else
{
ans=inf;
if(x==y&&(pos[x].size()||dis[x].size())) ans=0;
if(dis[x].size()) min(ans,dis[x][y]);
if(dis[y].size()) min(ans,dis[y][x]);
for(int i=0,j=0;i<(int)pos[x].size()||j<(int)pos[y].size();)
if(i<(int)pos[x].size()&&(j==(int)pos[y].size()||pos[x][i]<pos[y][j])) j? min(ans,pos[x][i]-pos[y][j-1]):void(),++i;
else i? min(ans,pos[y][j]-pos[x][i-1]):void(),++j;
ans==inf? puts("Ikaros"),ans=0:printf("%d
",ans);
}
}
}