[关闭]
@xiaoziyao 2020-10-29T04:09:12.000000Z 字数 4521 阅读 865

P5397 [Ynoi2018]天降之物解题报告

解题报告


P5397 [Ynoi2018]天降之物

更好的阅读体验

题意

分析

lxl的毒瘤大分块系列,弑尽破净的第四分块(好中二呀)。

先转换题意:对于每个值都有一个位置集合,支持合并集合(观察题意很容易看出来操作等价于合并集合),查询两个集合中差值最小的值。

我们先考虑两个暴力做法

但很显然这会时空双爆炸,考虑根号分治来平衡两种暴力的复杂度。

我们先对第一个暴力进行优化,来降低一下复杂度:

我们用维护所有的位置,并在其中从小到大排列,合并时可以归并做到,暴力枚举位置可以用两个指针不断推进,利用这个位置的单调性找出每次最近的两个位置更新答案,也可以做到

这个值在序列里出现的次数,并规定一个阈值,进行根号分治。

对于每个,我们保存一个类似缓存的型),博客里叫它附属集合,集合需要保存在上一次重构后所有修改操作中加入的新位置,并通过维护保证它的大小不大于,空间复杂度

对于所有位置集合大于,我们在每一次重构都预处理它里面每个值离其他值的最短距离,设为(位置集合离值的最短距离),可以发现这样的不超过个,因此我们的空间复杂度为

现在讲一讲具体操作:

对于修改(注意,这里我们可以进行一定的操作将交换,因此不妨设

对于查询(查询的顺序不影响答案,因此设

故程序的时间复杂度为,空间复杂度为,因为,所以的复杂度最优,时间复杂度,空间复杂度

代码

  1. #include<stdio.h>
  2. #include<vector>
  3. #include<string.h>
  4. #include<math.h>
  5. #define inf 0x3f3f3f3f
  6. using namespace std;
  7. const int maxn=1000005,maxt=505;
  8. int n,m,lastans,lim,tot;
  9. int a[maxn],val[maxn],size[maxn],id[maxn],ans[maxt][maxn];
  10. vector<int>v[maxn];
  11. inline int abs(int x){
  12. return x<0? -x:x;
  13. }
  14. void build(int x){
  15. int dis;
  16. if(id[x]==0)
  17. id[x]=++tot;
  18. memset(ans[id[x]],0x3f,sizeof(ans[id[x]]));
  19. v[x].clear();
  20. dis=inf;
  21. for(int i=1;i<=n;i++){
  22. if(a[i]==x)
  23. dis=0;
  24. else dis++;
  25. ans[id[x]][a[i]]=min(ans[id[x]][a[i]],dis);
  26. }
  27. dis=inf;
  28. for(int i=n;i>=1;i--){
  29. if(a[i]==x)
  30. dis=0;
  31. else dis++;
  32. ans[id[x]][a[i]]=min(ans[id[x]][a[i]],dis);
  33. }
  34. }
  35. void init(){
  36. lim=sqrt(n);
  37. for(int i=1;i<=n;i++)
  38. val[i]=n+1;
  39. for(int i=1;i<=n;i++)
  40. size[a[i]]++,v[a[i]].push_back(i),val[a[i]]=a[i];
  41. for(int i=1;i<=n;i++)
  42. if(size[i]>lim)
  43. build(i);
  44. }
  45. void merge(int x,int y){
  46. vector<int>res;
  47. for(int i=0,j=0;i<v[x].size()||j<v[y].size();){
  48. if(j>=v[y].size()||(i<v[x].size()&&v[x][i]<v[y][j]))
  49. res.push_back(v[x][i]),i++;
  50. else res.push_back(v[y][j]),j++;
  51. }
  52. v[y]=res;
  53. }
  54. void updateA(int x,int y){
  55. for(int i=0;i<v[x].size();i++)
  56. a[v[x][i]]=y;
  57. for(int i=1;i<=tot;i++)
  58. ans[i][y]=min(ans[i][y],ans[i][x]);
  59. merge(x,y);
  60. }
  61. void updateB(int x,int y){
  62. for(int i=1;i<=n;i++)
  63. if(a[i]==x)
  64. a[i]=y;
  65. build(y);
  66. }
  67. void update1(int x,int y){
  68. if(size[x]+size[y]<=lim)
  69. updateA(x,y);
  70. else updateB(x,y);
  71. }
  72. void update2(int x,int y){
  73. if(size[x]+v[y].size()<=lim)
  74. updateA(x,y);
  75. else updateB(x,y);
  76. }
  77. void update3(int x,int y){
  78. updateB(x,y);
  79. }
  80. void update(int x,int y){
  81. if(size[val[x]]==0||val[x]==val[y])
  82. return ;
  83. int px=val[x],py=val[y];
  84. if(size[val[x]]>size[val[y]])
  85. val[y]=val[x],swap(px,py);
  86. val[x]=n+1,x=px,y=py;
  87. if(x==n+1||y==n+1)
  88. return ;
  89. if(size[x]<=lim&&size[y]<=lim)
  90. update1(x,y);
  91. if(size[x]<=lim&&size[y]>lim)
  92. update2(x,y);
  93. if(size[x]>lim&&size[y]>lim)
  94. update3(x,y);
  95. size[y]+=size[x],size[x]=0;
  96. v[x].clear();
  97. }
  98. int calc(int x,int y){
  99. int i=0,j=0,res=inf;
  100. if(size[x]==0||size[y]==0)
  101. return inf;
  102. while(i<v[x].size()&&j<v[y].size()){
  103. if(v[x][i]<v[y][j])
  104. res=min(res,v[y][j]-v[x][i]),i++;
  105. else res=min(res,v[x][i]-v[y][j]),j++;
  106. }
  107. return res;
  108. }
  109. int query1(int x,int y){
  110. return calc(x,y);
  111. }
  112. int query2(int x,int y){
  113. return min(ans[id[y]][x],calc(x,y));
  114. }
  115. int query3(int x,int y){
  116. return min(min(ans[id[x]][y],ans[id[y]][x]),calc(x,y));
  117. }
  118. int query(int x,int y){
  119. x=val[x],y=val[y];
  120. if(x==n+1||y==n+1||size[x]==0||size[y]==0)
  121. return -1;
  122. if(x==y)
  123. return 0;
  124. if(size[x]>size[y])
  125. swap(x,y);
  126. if(size[x]<=lim&&size[y]<=lim)
  127. return query1(x,y);
  128. if(size[x]<=lim&&size[y]>lim)
  129. return query2(x,y);
  130. if(size[x]>lim&&size[y]>lim)
  131. return query3(x,y);
  132. return -1;
  133. }
  134. int main(){
  135. scanf("%d%d",&n,&m);
  136. for(int i=1;i<=n;i++)
  137. scanf("%d",&a[i]);
  138. init();
  139. for(int i=1;i<=m;i++){
  140. int t,x,y;
  141. scanf("%d%d%d",&t,&x,&y);
  142. x^=lastans,y^=lastans;
  143. if(t==1)
  144. update(x,y);
  145. if(t==2){
  146. int res=query(x,y);
  147. if(res==-1)
  148. lastans=0,puts("Ikaros");
  149. else lastans=res,printf("%d\n",res);
  150. }
  151. }
  152. return 0;
  153. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注