P5397 [Ynoi2018] 天降之物 题解
蒟蒻做的第一道黑色的 \(\text{Ynoi}\),写一篇题解纪念一下。
题意
第四分块。
你需要实现 \(m\) 个操作,操作有两种:
- 把序列中所有值为 \(x\) 的数的值变成 \(y\)。
- 找出一个位置 \(i\) 满足 \(a_i=x\),找出一个位置 \(j\) 满足 \(a_j=y\),使得 \(|i-j|\) 最小,并输出 \(|i-j|\)。
思路
考虑如何写一个暴力。
用一个 \(\text{vector}\) 把所有位置记录下来,然后每次查询用类似归并的写法暴力查询。
我们发现,这个东西可以用根号分治进行优化。
我们对每一个数的出现次数进行根号分治。
大体思路是对于出现次数大于阀值的预处理答案,查询时直接查。
对于出现次数小于阀值的直接暴力归并。
修改
首先有一个小的 \(\text{trick}\),就是我们发现 \(x\) 和 \(y\) 其实是等价的,所以我们可以直接交换他们。
考虑让 \(siz_x<siz_y\)。
- 若两个都大于阀值。
我们直接 \(O(n)\) 暴力合并,由于每一次合并都会减少一个出现次数大于阀值的数,所以时间复杂度为 \(O(n\sqrt n)\)
- 若一个大于阀值,一个小于阀值。
我们考虑对每一个大于阀值的维护一个附加集合。
我们将小于阀值的加入到附加集合中,在这里,再次考虑根号分治。
如果附加集合的大小小于阀值,那么,此部分复杂度为 \(O(m\sqrt n)\)。
如果附加集合的大小大于阀值,我们就全部暴力 \(O(n)\) 统计答案,这里最多会暴力重构 \(O(\sqrt n)\) 次。
所以,此部分复杂度为 \(O(m\sqrt n)\)。
- 若两个都小于阀值
我们将两个集合合并,在这里,如第二点一样考虑根号分治。
如果集合的大小小于阀值,那么,此部分复杂度为 \(O(m\sqrt n)\)。
如果集合的大小大于阀值,我们就全部暴力 \(O(n)\) 统计答案,这里最多会暴力重构 \(O(\sqrt n)\) 次。
所以,此部分复杂度为 \(O(m\sqrt n)\)。
查询
查询比上面的修改有简单多了,毕竟答案什么都预处理完了。
- 若两个都大于阀值。
则查询预处理的答案,并且将两个附加集合进行归并计算,时间复杂度 \(O(m \sqrt n)\)。
- 若一个大于阀值,一个小于阀值。
同样查询预处理的答案,并且将附加集合和集合进行归并计算,时间复杂度 \(O(m \sqrt n)\)。
- 若两个都小于阀值
直接归并计算即可,时间复杂度 \(O(m\sqrt n)\)。
由于 \(m,n\) 同阶,所以综上,时间复杂度为 \(O(n\sqrt n)\)
代码实测阀值取 \(500\) 跑得最快。
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
const int base = 320;
int n , m , top , len , lastans , a[N] , stk[N] , siz[N] , rel[N] , sum[base][N];
vector<int> f[N];
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
inline void cl(vector<int> &now)
{
vector<int> ne;
now.swap(ne);
}
inline int calc(vector<int> x , vector<int> y)
{
int len1 = x.size() , len2 = y.size() , ans = 100010;
for(int i = 0 , j = 0;i < len1;i++)
{
if(j < len2) ans = min(ans , abs(y[j] - x[i]));
while(j < len2 && y[j] < x[i]) ans = min(ans , x[i] - y[j++]);
if(j < len2) ans = min(ans , abs(y[j] - x[i]));
}
return ans;
}
inline void solve(vector<int> &x , vector<int> &y)
{
vector<int> ne;
int i , j , len1 = x.size() , len2 = y.size() , ans = 100010;
for(i = 0 , j = 0;i < len1;i++)
{
while(j < len2 && y[j] < x[i]) ne.push_back(y[j++]);
ne.push_back(x[i]);
}
while(j < len2) ne.push_back(y[j++]);
y = ne , cl(x);
}
inline void update(int r , int id = 0)
{
stk[r] = stk[r] ? stk[r] : ++top; int x = stk[r];
memset(sum[x] , 0x3f , sizeof sum[x]);
for(int i = 1 , j = N;i <= n;i++)
if(a[i] == r) j = 0; else sum[x][a[i]] = min(sum[x][a[i]] , ++j);
for(int i = n , j = N;i >= 1;i--)
if(a[i] == r) j = 0; else sum[x][a[i]] = min(sum[x][a[i]] , ++j);
cl(f[r]);
}
int main()
{
n = read() , m = read() , memset(sum , 0x3f , sizeof sum) , len = 500;
for(int i = 1;i <= n;i++) a[i] = read() , rel[i] = i;
for(int i = 1;i <= n;i++) f[a[i]].push_back(i) , siz[a[i]]++;
for(int i = 1;i <= n;i++) if(f[i].size() > len) update(i);
for(int i = 1;i <= m;i++)
{
int opt = read() , x = read() ^ lastans , y = read() ^ lastans;
if(opt == 1)
{
int l = rel[x] , r = rel[y];
if(siz[l] == 0 || l == r) continue;
if(siz[l] > siz[r]) rel[y] = l , swap(l , r) , rel[x] = n + 1;
else rel[x] = n + 1; if(l > n || r > n) continue;
if(siz[r] <= len)
{
if(siz[l] + siz[r] <= len)
{
for(int j : f[l]) a[j] = r;
for(int j = 1;j <= top;j++) sum[j][r] = min(sum[j][r] , sum[j][l]);
solve(f[l] , f[r]);
}
else
{
for(int i = 1;i <= n;i++) if(a[i] == l) a[i] = r;
update(r);
}
}
else if(siz[l] <= len)
{
if(siz[l] + f[r].size() <= len)
{
for(int j : f[l]) a[j] = r;
for(int j = 1;j <= top;j++) sum[j][r] = min(sum[j][r] , sum[j][l]);
solve(f[l] , f[r]);
}
else
{
for(int i = 1;i <= n;i++) if(a[i] == l) a[i] = r;
update(r);
}
}
else
{
for(int i = 1;i <= n;i++) if(a[i] == l) a[i] = r;
update(r);
}
cl(f[l]) , siz[r] += siz[l] , siz[l] = 0;
}
else
{
int l = rel[x] , r = rel[y];
if(!siz[l] || !siz[r]) lastans = -1;
else if(l == r) lastans = 0;
else
{
if(siz[l] > siz[r]) swap(l , r);
if(siz[r] <= len) lastans = calc(f[l] , f[r]);
else if(siz[l] <= len) lastans = min(sum[stk[r]][l] , calc(f[l] , f[r]));
else lastans = min({sum[stk[r]][l] , sum[stk[l]][r] , calc(f[l] , f[r])});
}
if(lastans == -1) puts("Ikaros") , lastans = 0;
else printf("%d\n" , lastans);
}
}
return 0;
}

浙公网安备 33010602011771号