Loading

P5397 [Ynoi2018] 天降之物 题解

蒟蒻做的第一道黑色的 \(\text{Ynoi}\),写一篇题解纪念一下。

题意

第四分块。

你需要实现 \(m\) 个操作,操作有两种:

  1. 把序列中所有值为 \(x\) 的数的值变成 \(y\)
  2. 找出一个位置 \(i\) 满足 \(a_i=x\),找出一个位置 \(j\) 满足 \(a_j=y\),使得 \(|i-j|\) 最小,并输出 \(|i-j|\)

思路

考虑如何写一个暴力。

用一个 \(\text{vector}\) 把所有位置记录下来,然后每次查询用类似归并的写法暴力查询。

我们发现,这个东西可以用根号分治进行优化。

我们对每一个数的出现次数进行根号分治。

大体思路是对于出现次数大于阀值的预处理答案,查询时直接查。

对于出现次数小于阀值的直接暴力归并。

修改

首先有一个小的 \(\text{trick}\),就是我们发现 \(x\)\(y\) 其实是等价的,所以我们可以直接交换他们。

考虑让 \(siz_x<siz_y\)

  1. 若两个都大于阀值。

我们直接 \(O(n)\) 暴力合并,由于每一次合并都会减少一个出现次数大于阀值的数,所以时间复杂度为 \(O(n\sqrt n)\)

  1. 若一个大于阀值,一个小于阀值。

我们考虑对每一个大于阀值的维护一个附加集合。

我们将小于阀值的加入到附加集合中,在这里,再次考虑根号分治。

如果附加集合的大小小于阀值,那么,此部分复杂度为 \(O(m\sqrt n)\)

如果附加集合的大小大于阀值,我们就全部暴力 \(O(n)\) 统计答案,这里最多会暴力重构 \(O(\sqrt n)\) 次。

所以,此部分复杂度为 \(O(m\sqrt n)\)

  1. 若两个都小于阀值

我们将两个集合合并,在这里,如第二点一样考虑根号分治。

如果集合的大小小于阀值,那么,此部分复杂度为 \(O(m\sqrt n)\)

如果集合的大小大于阀值,我们就全部暴力 \(O(n)\) 统计答案,这里最多会暴力重构 \(O(\sqrt n)\) 次。

所以,此部分复杂度为 \(O(m\sqrt n)\)

查询

查询比上面的修改有简单多了,毕竟答案什么都预处理完了。

  1. 若两个都大于阀值。

则查询预处理的答案,并且将两个附加集合进行归并计算,时间复杂度 \(O(m \sqrt n)\)

  1. 若一个大于阀值,一个小于阀值。

同样查询预处理的答案,并且将附加集合和集合进行归并计算,时间复杂度 \(O(m \sqrt n)\)

  1. 若两个都小于阀值

直接归并计算即可,时间复杂度 \(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;
}
posted @ 2022-03-14 19:54  JiaY19  阅读(86)  评论(0)    收藏  举报