P4119 [Ynoi2018] 未来日记 题解

最初分块,个人感觉如果之前做过带插入区间 K 小值和第二分块的话,应该还比较好想。

思路#

由于要询问的是区间第 k 小值,自然而然的就可以想到分块套分块,外层对序列进行分块,内层运用值域分块,空间复杂度:O(nn)

具体的,我们对于序列上的每一个块维护两个数组。

第一个是每个数对于每个块的前缀和。

第二个是对第一个数组进行的分块。

所以对于一个区间的第 k 小,我们先将两边的散块暴力插入一个桶中,然后找到左右端点,依次判断每一个值域所分的块。

找到之后,再在块中扫一遍。

由于最多只有 n 个块,每个块内也只有 n 个数。

所以时间复杂度 O(n)

我们现在来考虑如何实现修改:

把区间 [l,r] 中所有 x 变成 y

如果做过第二分块的话就可以想到用并查集处理这个问题。

对于散块,我们直接重构出整个块的并查集关系。

由于改变的只有 xy 的个数,所以虽然其他的并查集关系有可能会改变,但在值域数组中的大小是不会改变的,所以只需要更改 xy 的大小即可。

我们暴力统计更改后出这一块内 xy 的个数,再暴力更改后面的每一个块,由于更改每一个块只需要 O(1)

所以时间复杂度为 O(n)

至于整块,我们可以对于每一个块直接更改并查集,由于最多 n 个块。

所以时间复杂度为 O(Tn),其中 T 为并查集所用时间。

总时间复杂度 O(nTn)

但这个代码还不足以通过此题,你只能获得 64pts 或者 73pts

我们发现如果是左右端点不同块的修改,对于散块需要有两倍的常数,所以我们考虑对散块进行进一步的优化。

由于在散块内并查集更改的只有权值为 xy 的数。

所以我们只更改他们的并查集关系,不直接重构整块。

时间复杂度不变,但足以通过此题了。

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;
const int len =  430;
const int bas = 100000;

int n , m , top , a[N] , s[N] , h[N] , q[N] , l[N] , r[N] , ch[N] , fa[N] , pos[N] , stk[N];

struct Node
{
    int a[N] , ton[N] , siz[len + 10];
}b[len + 100];

#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
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 int gf(int x)
{
    return fa[x] == x ? x : fa[x] = gf(fa[x]);
}

inline int merge(int x , int y , int pos)
{
    if(b[pos].ton[y]) fa[b[pos].ton[x]] = b[pos].ton[y];
    else b[pos].ton[y] = b[pos].ton[x] , s[b[pos].ton[x]] = y;
    b[pos].ton[x] = 0; return b[pos].a[x] - b[pos - 1].a[x];
}

inline void update(int x , int y , int ls , int rs)
{
    int posl = pos[ls] , res = 0; top = 0;
    b[posl].ton[x] = b[posl].ton[y] = 0;
    for(int i = l[posl];i <= min(r[posl] , n);i++)
    {
        a[i] = s[gf(i)];
        if(a[i] == x || a[i] == y) stk[++top] = i;
    }
    for(int i = ls;i <= rs;i++)
        if(a[i] == x) res++ , a[i] = y;
    for(int i = 1;i <= top;i++)
    {
        int j = stk[i];
        if(!b[posl].ton[a[j]]) b[posl].ton[a[j]] = fa[j] = j , s[j] = a[j];
        else fa[j] = b[posl].ton[a[j]];
    }
    for(int i = posl;i <= pos[n];i++)
        b[i].siz[pos[x]] -= res , b[i].a[x] -= res,
        b[i].siz[pos[y]] += res , b[i].a[y] += res;
}

inline void delet(int x)  { h[x]-- , q[pos[x]]--; }
inline void insert(int x) { h[x]++ , q[pos[x]]++; }

int main()
{
    int fir = 0;
    n = read() , m = read();
    for(int i = 1;i <= bas;i++) pos[i] = (i - 1) / len + 1 , r[pos[i]] = i;
    for(int i = 1;i <= bas;i++) l[pos[i]] = (l[pos[i]] ? l[pos[i]] : i);
    for(int i = 1;i <= n;i++) a[i] = read();
    for(int i = 1;i <= pos[n];i++)
        for(int j = l[i];j <= min(r[i] , n);j++)
            b[i].a[a[j]]++ , b[i].siz[pos[a[j]]]++;
    for(int i = 1;i <= pos[n];i++)
        for(int j = l[i];j <= min(r[i] , n);j++)
            if(!b[i].ton[a[j]]) b[i].ton[a[j]] = fa[j] = j , s[j] = a[j];
            else fa[j] = b[i].ton[a[j]];
    for(int i = 1;i <= pos[n];i++)
    {
        for(int j = 1;j <= bas;j++)
            b[i].a[j] += b[i - 1].a[j];
        for(int j = 1;j <= pos[bas];j++)
            b[i].siz[j] += b[i - 1].siz[j];
    }
    for(int o = 1;o <= m;o++)
    {
        int opt = read();
        if(opt == 1)
        {
            int ls = read() , rs = read() , x = read() , y = read();
            if(x == y) continue;
            if(pos[ls] == pos[rs]) update(x , y , ls , rs);
            else
            {
                int res = 0;
                update(x , y , ls , r[pos[ls]]) , update(x , y , l[pos[rs]] , rs);
                for(int i = pos[ls] + 1;i <= pos[rs] - 1;i++)
                    ch[i] = merge(x , y , i);
                for(int i = pos[ls] + 1;i <= pos[rs] - 1;i++)
                    res += ch[i] , b[i].a[x] -= res , b[i].a[y] += res,
                    b[i].siz[pos[x]] -= res , b[i].siz[pos[y]] += res;
                for(int i = pos[rs];i <= pos[n];i++)
                    b[i].a[x] -= res , b[i].a[y] += res,
                    b[i].siz[pos[x]] -= res , b[i].siz[pos[y]] += res;
            }
        }
        if(opt == 2)
        {
            int ls = read() , rs = read() , k = read() , now = 1 , res;
            if(pos[ls] == pos[rs])
            {
                for(int i = ls;i <= rs;i++) 
                {
                    insert(s[gf(i)]);
                }
                for(int i = 1;i <= pos[bas];i++)
                    if(k <= q[i]) break;
                    else k -= q[i] , now++;
                for(int i = l[now];i <= r[now];i++)
                    if(k <= h[i]) { res = i; break; }
                    else k -= h[i];
                for(int i = ls;i <= rs;i++) delet(s[gf(i)]);
            }
            else
            {
                int bl = pos[ls] , br = pos[rs] - 1;
                for(int i = ls;i <= r[pos[ls]];i++) insert(s[gf(i)]);
                for(int i = l[pos[rs]];i <= rs;i++) insert(s[gf(i)]);
                for(int i = 1;i <= pos[bas];i++)
                    if(k <= q[i] + b[br].siz[i] - b[bl].siz[i]) break;
                    else k -= q[i] + b[br].siz[i] - b[bl].siz[i] , now++;
                for(int i = l[now];i <= r[now];i++)
                    if(k <= h[i] + b[br].a[i] - b[bl].a[i]) { res = i; break; }
                    else k -= h[i] + b[br].a[i] - b[bl].a[i];
                for(int i = ls;i <= r[pos[ls]];i++) delet(s[gf(i)]);
                for(int i = l[pos[rs]];i <= rs;i++) delet(s[gf(i)]);
            }
            printf("%d\n" , res);
        }
    }
    return 0;
}

作者:JiaY19

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

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

posted @   JiaY19  阅读(65)  评论(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
点击右上角即可分享
微信分享提示