题解 P4119 [Ynoi2018] 未来日记

· · 题解

upd:2021/5/4 被 mcyl35 叉掉了,原因是值域分块值域开小了,在这里感谢他的 hack 。

应该说只要做过第二分块,这道题基本上就没有什么有趣的地方了,我愿称第二分块为最初分块前体

我们首先考虑一下这个修改操作,不难发现他的本质就是映射,感觉可以维护的方式有点多,先咕着。

查询是区间 rank ,我第一想法就是由乃打扑克,每个块排序然后 lowerbound 查询,但是这样的话就不好修改了,我们得考虑一种能兼容修改的查询方法。

我们不难发现对于 rank ,我们有一个很普遍的值域分块方法,如果此题中我们对每个块进行值域分块,维护下来块的前缀值域情况,那么这道题就很简单了。

我们又发现,修改的映射每一次只会修改两类数,这个显然可以数组存储做到 O(1),然后我们把暴力修改涉及到的两类数所影响的前缀推平一遍就好了,这个过程显然 O(\sqrt n)

接着我们思考一个问题:我们的修改在值域分块的维护上面没有问题,但是对于散块修改呢?但是怎么维护整块修改完后每个数变成了什么?

这时候就得并查集了,对于整块我们还要用并查集维护它们的映射关系。

记录 fr[x][y] 表示权值 y 在块 x 内第一个出现的位置,相当于是一个捆绑标记,col[x] 表示序列里面第 x 个数的颜色,令当前块为 t 。

我们判断 a 改为 b 的映射,如果 a 不存在,那么我们不要这个修改。如果 b 不存在,我们就直接让 b 的 fr[t][b] = fr[t][a} 即可,并且把 col[fr[t][b]] 修改为 b ,否则都出现过的话直接并查集,把 fr[t][a]父亲置为 fr[t][b] 就好了。

接着我们对于整块直接并查集进去,对于散块,看着不爽直接重构就好了,时间复杂度 O(n \sqrt n)

不过此题轻度卡常,建议多玄学剪枝一些东西,建议先做第二分块。

不过要我评,做这道题之前就算我没做过第二分块,估计也只能给 6 的评分。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int Len = 1e5 + 5 , SIZE = 330;
int n,m,fr[SIZE][Len],col[Len],fa[Len],cnt[SIZE][Len],sum[SIZE][SIZE],vt,t,L[SIZE],R[SIZE],pos[Len],VL[SIZE],VR[SIZE],Vpos[Len],a[Len];
void makeSet(int x){for(int i = 1 ; i <= x ; i ++) fa[i] = i;}
int findSet(int x){return fa[x] == x ? fa[x] : fa[x] = findSet(fa[x]);}
inline void merge(int x,int X,int Y)
{
    if(!fr[x][X]) return;
    else if(!fr[x][Y]){fr[x][Y] = fr[x][X] , col[fr[x][Y]] = Y;}
    else fa[fr[x][X]] = fr[x][Y];
    fr[x][X] = 0;
}
inline void build(int x,int l,int r,int X,int Y)
{
    for(int i = L[x] ; i <= R[x] ; i ++){a[i] = col[findSet(i)] ; fr[x][a[i]] = 0;}
    for(int i = l ; i <= r ; i ++) if(a[i] == X) a[i] = Y;
    for(int i = L[x] ; i <= R[x] ; i ++) 
    {
        if(!fr[x][a[i]]){fr[x][a[i]] = i;col[fr[x][a[i]]] = a[i];}
        fa[i] = fr[x][a[i]];
    }
}
inline void upd(int l,int r,int X,int Y)
{
    if(X == Y) return;
    int LL = pos[l] , RR = pos[r];
    int numb = 0 , Tmp = 0;
    if(LL == RR) 
    {
        for(int i = l ; i <= r ; i ++) if(col[findSet(i)] == X) numb ++;
        if(!numb) return;
        for(int i = LL ; i <= t ; i ++) sum[i][Vpos[X]] -= numb , sum[i][Vpos[Y]] += numb , cnt[i][X] -= numb , cnt[i][Y] += numb;
        build(LL , l , r , X , Y);
        return;
    }
    for(int i = l ; i <= R[LL] ; i ++) if(col[findSet(i)] == X) numb ++;
    if(numb) build(LL , l , R[LL] , X , Y);
    for(int i = LL ; i <= RR - 1 ; i ++) numb += Tmp , sum[i][Vpos[X]] -= numb , sum[i][Vpos[Y]] += numb , Tmp = cnt[i + 1][X] - cnt[i][X] , cnt[i][X] -= numb , cnt[i][Y] += numb; 
    int v = 0;for(int i = L[RR] ; i <= r ; i ++) if(col[findSet(i)] == X) numb ++ , v ++;
    for(int i = RR ; i <= t ; i ++) sum[i][Vpos[X]] -= numb , sum[i][Vpos[Y]] += numb , cnt[i][X] -= numb , cnt[i][Y] += numb; 
    for(int i = LL + 1 ; i <= RR - 1 ; i ++) merge(i , X , Y);
    if(v) build(RR , L[RR] , r , X , Y);
    return;
}
int UsedSum[Len],UsedBlock[SIZE];
inline int qry(int l,int r,int k)
{
    int LL = pos[l] , RR = pos[r];
    if(k > r - l + 1) return -1;
    if(LL == RR)
    {
        for(int i = l ; i <= r ; i ++) UsedBlock[Vpos[col[findSet(i)]]] ++ , UsedSum[col[findSet(i)]] ++;
        for(int j = 1 ; j <= vt ; j ++) 
        {
            if(k > UsedBlock[j]) k -= UsedBlock[j];
            else
            {
                for(int p = VL[j] ; p <= VR[j] ; p ++) 
                {
                    if(k > UsedSum[p]) k -= UsedSum[p];
                    else 
                    {
                        for(int i = l ; i <= r ; i ++) UsedBlock[Vpos[col[findSet(i)]]] -- , UsedSum[col[findSet(i)]] --;
                        return p;
                    }
                }
            }
        }
    }
    for(int i = l ; i <= R[LL] ; i ++) UsedBlock[Vpos[col[findSet(i)]]] ++ , UsedSum[col[findSet(i)]] ++;
    for(int i = L[RR] ; i <= r ; i ++) UsedBlock[Vpos[col[findSet(i)]]] ++ , UsedSum[col[findSet(i)]] ++;
    for(int j = 1 ; j <= vt ; j ++)
    {
        if(k > (sum[RR - 1][j] - sum[LL][j] + UsedBlock[j])) k -= sum[RR - 1][j] - sum[LL][j] + UsedBlock[j];
        else
        {
            for(int p = VL[j] ; p <= VR[j] ; p ++)
            {
                if(k > (cnt[RR - 1][p] - cnt[LL][p] + UsedSum[p])) k -= cnt[RR - 1][p] - cnt[LL][p] + UsedSum[p];
                else 
                {
                    for(int i = l ; i <= R[LL] ; i ++) UsedBlock[Vpos[col[findSet(i)]]] -- , UsedSum[col[findSet(i)]] --;
                    for(int i = L[RR] ; i <= r ; i ++) UsedBlock[Vpos[col[findSet(i)]]] -- , UsedSum[col[findSet(i)]] --;
                    return p;
                }
            }
        }
    }
}
struct Node
{
    int opt,l,r,x,y;    
}lxlNB[Len]; 
signed main()
{
    int maxn = 100000;
    scanf("%d %d",&n,&m);
    makeSet(n);
    t = sqrt(n);
    for(int i = 1 ; i <= n ; i ++){scanf("%d",&a[i]) ; maxn = max(maxn , a[i]);}
    for(int i = 1 ; i <= m ; i ++)
    {
        scanf("%d %d %d %d",&lxlNB[i].opt,&lxlNB[i].l,&lxlNB[i].r,&lxlNB[i].x);
        if(lxlNB[i].opt == 1) 
        {
            scanf("%d",&lxlNB[i].y);
            maxn = max(maxn , lxlNB[i].x) , maxn = max(maxn , lxlNB[i].y);
        }
    }
    vt = sqrt(maxn);
    for(int i = 1 ; i <= t ; i ++) L[i] = (i - 1) * t + 1 , R[i] = i * t;
    R[t] = n;
    for(int i = 1 ; i <= vt ; i ++) VL[i] = (i - 1) * vt + 1 , VR[i] = i * vt;
    VR[vt] = maxn;
    for(int i = 1 ; i <= t ; i ++) 
        for(int j = L[i] ; j <= R[i] ; j ++) pos[j] = i;
    for(int i = 1 ; i <= vt ; i ++) 
        for(int j = VL[i] ; j <= VR[i] ; j ++) Vpos[j] = i;
    for(int i = 1 ; i <= t ; i ++)
    {
        for(int j = L[i] ; j <= R[i] ; j ++)
        {
            if(!fr[i][a[j]]){fr[i][a[j]] = j ; col[fr[i][a[j]]] = a[j];}
            fa[j] = fr[i][a[j]];
            cnt[i][a[j]] ++ , sum[i][Vpos[a[j]]] ++;
        }
        for(int j = 1 ; j <= vt ; j ++) sum[i][j] += sum[i - 1][j];
        for(int j = 1 ; j <= maxn ; j ++) cnt[i][j] += cnt[i - 1][j]; 
    }
    for(int i = 1 ; i <= m ; i ++) 
    {
        if(lxlNB[i].opt == 1) upd(lxlNB[i].l , lxlNB[i].r , lxlNB[i].x , lxlNB[i].y);
        else printf("%d\n",qry(lxlNB[i].l , lxlNB[i].r , lxlNB[i].x));
    }
    return 0;
}