Loading

P4119 [Ynoi2018] 未来日记 题解

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

思路

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

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

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

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

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

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

由于最多只有 \(\sqrt n\) 个块,每个块内也只有 \(\sqrt n\) 个数。

所以时间复杂度 \(O(\sqrt n)\)

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

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

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

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

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

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

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

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

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

总时间复杂度 \(O(nT\sqrt n)\)

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

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

由于在散块内并查集更改的只有权值为 \(x\)\(y\) 的数。

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

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

#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;
}

posted @ 2022-03-25 17:28  JiaY19  阅读(58)  评论(0编辑  收藏  举报