P5397 [Ynoi2018] 天降之物 题解

蒟蒻做的第一道黑色的 Ynoi,写一篇题解纪念一下。

题意#

第四分块。

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

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

思路#

考虑如何写一个暴力。

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

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

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

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

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

修改

首先有一个小的 trick,就是我们发现 xy 其实是等价的,所以我们可以直接交换他们。

考虑让 sizx<sizy

  1. 若两个都大于阀值。

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

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

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

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

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

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

所以,此部分复杂度为 O(mn)

  1. 若两个都小于阀值

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

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

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

所以,此部分复杂度为 O(mn)

查询

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

  1. 若两个都大于阀值。

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

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

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

  1. 若两个都小于阀值

直接归并计算即可,时间复杂度 O(mn)

由于 m,n 同阶,所以综上,时间复杂度为 O(nn)

代码实测阀值取 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;
}

作者:JiaY19

出处:https://www.cnblogs.com/JiaY19/p/16005691.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   JiaY19  阅读(73)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示