题目背景
可曾做过这样的梦呢?
在我身边,有一个我从没见过的少女
我很喜欢她,她也很喜欢我
不过——
最后总是
在她被天空掳去的时候
迎来梦醒时分
人?
不对
不是人
没有人会...
长着翅膀
没有没有没有
哪里都没有
没有我可以回去的地方
我没有任何可以回去的地方!
哪里都没有...
我能回去的地方...无论哪里...无论哪里都没有...
我不想杀死任何人,不想伤害任何人!
为什么...为什么要制造出我...
不需要...我不需要这种力量...
幸福不足以形容我现在的心情
我该说什么才好呢
啊对了
我...爱你
我爱你...主人
神话中的伊卡洛斯,由于过于靠近太阳,用蜡粘住的羽毛融化后坠海而死
题目描述
伊卡洛斯给了你一个长为 �n 的序列 �a。
你需要实现 �m 个操作,操作有两种:
- 把序列中所有值为 �x 的数的值变成 �y。
- 找出一个位置 �i 满足 ��=�ai=x,找出一个位置 �j 满足 ��=�aj=y,使得 ∣�−�∣∣i−j∣ 最小,并输出 ∣�−�∣∣i−j∣。
输入格式
第一行两个整数 �,�n,m。
之后一行 �n 个整数,表示序列 �a。
之后 �m 行,每行三个数 ���,�,�opt,x,y。
如果 ���opt 为 11,代表把序列中所有值为 �x 位置的值变成 �y。
如果 ���opt 为 22,代表找出一个位置 �i 满足 ��=�ai=x,找出一个位置 �j 满足 ��=�aj=y,使得 ∣�−�∣∣i−j∣ 最小,并输出 ∣�−�∣∣i−j∣,如果找不出这样的位置,输出 Ikaros
。
本题强制在线,每次的 �,�x,y 需要 xor 上上次答案,如果输出 Ikaros
,或者是第一次询问,则上次答案为 00。
共 5050 组数据,数据中保证 �=�n=m。
输出格式
对于每个 22 操作,输出一行一个整数表示答案。
如果无法找出满足题意的 �,�i,j,则输出 Ikaros
。
输入输出样例
输入 #1复制
5 5 1 2 2 4 4 2 3 3 2 2 4 1 3 2 1 5 5 2 2 5
输出 #1复制
Ikaros 1 1
说明/提示
Idea:nzhtl1477,Solution:nzhtl1477,Code:nzhtl1477,Data:nzhtl1477( partially uploaded )
对于 100%100% 的数据,所有数在 [1,105][1,105] 内,每次操作的值不超过 �n。
题意
- 给定一个长为�n的序列�a,一共�m个操作:
- 操作11:把序列里所有值为�x的数改为�y;
- 操作22:求序列中值为�x的位置和值为�y的位置距离的最小值,如果找不到这样的位置输出
Ikaros
。 - 强制在线,每次输入的�x和�y需要异或上一次的答案,如果这是第一次输出或上一次输出
Ikaros
,则不需要异或。 - 数据范围:1⩽�=�⩽1051⩽n=m⩽105,其他所有数在[1,�][1,n]内。
分析
lxl的毒瘤大分块系列,弑尽破净的第四分块(好中二呀)。
先转换题意:对于每个值都有一个位置集合,支持合并集合(观察题意很容易看出来操作11等价于合并集合),查询两个集合中差值最小的值。
我们先考虑两个暴力做法
- 维护每一个值的所有位置,修改就把整个位置集合合并,查询就暴力枚举位置。
- 预处理每一个值离所有其他值的最短距离(这里的预处理可以通过从前到后扫一遍,从后到前扫一遍,做到�(�)O(n)的实现),修改时�(�)O(n)暴力重构,查询�(1)O(1)查询。
但很显然这会时空双爆炸,考虑根号分治来平衡两种暴力的复杂度。
我们先对第一个暴力进行优化,来降低一下复杂度:
我们用vectorvector维护所有的位置,并在其中从小到大排列,合并时可以归并做到�(�)O(n),暴力枚举位置可以用两个指针不断推进,利用这个位置的单调性找出每次最近的两个位置更新答案,也可以做到�(�)O(n)。
设�����sizex为�x这个值在序列里出现的次数,并规定一个阈值���lim,进行根号分治。
对于每个�x,我们保存一个类似缓存的��vx(vectorvector型),lxllxl博客里叫它附属集合,�v集合需要保存在上一次重构后所有修改操作中加入的新位置,并通过维护保证它的大小不大于���lim,空间复杂度�(�⋅���)O(n⋅lim)。
对于所有位置集合大于���lim的�x,我们在每一次重构都预处理它里面每个值离其他值的最短距离,设为����,�ansx,y(位置集合�x离值�y的最短距离),可以发现这样的�x不超过����limn个,因此我们的空间复杂度为�(�⋅���)O(n⋅lim)。
现在讲一讲具体操作:
对于修改(注意,这里我们可以进行一定的操作将�x与�y交换,因此不妨设�����⩽�����sizex⩽sizey)
- 当�����⩽���,�����⩽���sizex⩽lim,sizey⩽lim时
-
- 若�����+�����⩽���sizex+sizey⩽lim,我们就暴力合并�x和�y的�v集合,每一次复杂度为�(���)O(lim),总复杂度为�(�⋅���)O(m⋅lim)。
-
- 若�����+�����>���sizex+sizey>lim,那么我们预处理���ans,并清空�v集合,很容易发现这样的操作不超过����limn次,而每一次预处理���ans是�(�)O(n),因此总复杂度是�(�2���)O(limn2)。
- 当�����⩽���,�����>���sizex⩽lim,sizey>lim时
-
- 若�����+�[�].����()⩽���sizex+v[y].size()⩽lim,我们暴力把�x加入到�y的�v集合中,总复杂度�(�⋅���)O(m⋅lim)。
-
- 若�����+�[�].����()>���sizex+v[y].size()>lim,重构�y的���ans集合,加入�x和�[�]v[y]的贡献,并清空�y的�v集合,操作数显然不超过����limn,复杂度为�(�2���)O(limn2)。
- 当�����>���,�����>���sizex>lim,sizey>lim时,直接暴力将�x的所有位置合并到�y上面,单次复杂度�(�)O(n),总复杂度�(�2���)O(limn2),理由同上。
对于查询(查询的�x和�y顺序不影响答案,因此设�����⩽�����sizex⩽sizey)
- 当�����⩽���,�����⩽���sizex⩽lim,sizey⩽lim时,我们采用上述暴力11来计算答案,复杂度为�(���)O(lim),总复杂度�(�⋅���)O(m⋅lim)。
- 当�����⩽���,�����>���sizex⩽lim,sizey>lim时,采用上述暴力计算�x的位置集合和�y的�v集合的答案,同时别忘了加上���[�]ans[y]的贡献,总复杂度�(�⋅���)O(m⋅lim)。
- 当�����>���,�����>���sizex>lim,sizey>lim时,采用上述暴力计算�x和�y的�v集合的答案,加上���[�]ans[x]和���[�]ans[y]的贡献,总复杂度�(�⋅���)O(m⋅lim)。
故程序的时间复杂度为�(�2���+�⋅���)O(limn2+m⋅lim),空间复杂度为�(�⋅���)O(n⋅lim),因为�=�n=m,所以���=�lim=n的复杂度最优,时间复杂度�(��)O(nn),空间复杂度�(��)O(nn)。
代码如下:
#include<stdio.h>
#include<vector>
#include<string.h>
#include<math.h>
#define inf 0x3f3f3f3f
using 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){//暴力1
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){//暴力2
for(int i=1;i<=n;i++)
if(a[i]==x)
a[i]=y;
build(y);
}
void update1(int x,int y){//修改-分类讨论1
if(size[x]+size[y]<=lim)
updateA(x,y);
else updateB(x,y);
}
void update2(int x,int y){//修改-分类讨论2
if(size[x]+v[y].size()<=lim)
updateA(x,y);
else updateB(x,y);
}
void update3(int x,int y){//修改-分类讨论3
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){//查询-分类讨论1
return calc(x,y);
}
int query2(int x,int y){//查询-分类讨论2
return min(ans[id[y]][x],calc(x,y));
}
int query3(int x,int y){//查询-分类讨论3
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;
}
拜拜!