@xiaoziyao
        
        2020-10-29T04:09:12.000000Z
        字数 4521
        阅读 1545
    解题报告
Ikaros。Ikaros,则不需要异或。lxl的毒瘤大分块系列,弑尽破净的第四分块(好中二呀)。
先转换题意:对于每个值都有一个位置集合,支持合并集合(观察题意很容易看出来操作等价于合并集合),查询两个集合中差值最小的值。
我们先考虑两个暴力做法
但很显然这会时空双爆炸,考虑根号分治来平衡两种暴力的复杂度。
我们先对第一个暴力进行优化,来降低一下复杂度:
我们用维护所有的位置,并在其中从小到大排列,合并时可以归并做到,暴力枚举位置可以用两个指针不断推进,利用这个位置的单调性找出每次最近的两个位置更新答案,也可以做到。
设为这个值在序列里出现的次数,并规定一个阈值,进行根号分治。
对于每个,我们保存一个类似缓存的(型),博客里叫它附属集合,集合需要保存在上一次重构后所有修改操作中加入的新位置,并通过维护保证它的大小不大于,空间复杂度。
对于所有位置集合大于的,我们在每一次重构都预处理它里面每个值离其他值的最短距离,设为(位置集合离值的最短距离),可以发现这样的不超过个,因此我们的空间复杂度为。
现在讲一讲具体操作:
对于修改(注意,这里我们可以进行一定的操作将与交换,因此不妨设)
对于查询(查询的和顺序不影响答案,因此设)
故程序的时间复杂度为,空间复杂度为,因为,所以的复杂度最优,时间复杂度,空间复杂度。
#include<stdio.h>#include<vector>#include<string.h>#include<math.h>#define inf 0x3f3f3f3fusing namespace std;const int maxn=1000005,maxt=505;int n,m,lastans,lim,tot;int a[maxn],val[maxn],size[maxn],id[maxn],ans[maxt][maxn];vector<int>v[maxn];inline int abs(int x){return x<0? -x:x;}void build(int x){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++)val[i]=n+1;for(int i=1;i<=n;i++)size[a[i]]++,v[a[i]].push_back(i),val[a[i]]=a[i];for(int i=1;i<=n;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){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){for(int i=1;i<=n;i++)if(a[i]==x)a[i]=y;build(y);}void update1(int x,int y){if(size[x]+size[y]<=lim)updateA(x,y);else updateB(x,y);}void update2(int x,int y){if(size[x]+v[y].size()<=lim)updateA(x,y);else updateB(x,y);}void update3(int x,int y){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++;}return res;}int query1(int x,int y){return calc(x,y);}int query2(int x,int y){return min(ans[id[y]][x],calc(x,y));}int query3(int x,int y){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);return -1;}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;}