luoguP4119 【[Ynoi2018]未来日记】

这道题简直了...

码力极弱的我前前后后调了半个月...后来还是熬了一个通宵重构代码才A的

注意:

本篇题解内,值域与\(n\)同阶,皆表示为\(O(n)\)

思路:

看到Ynoi和\(10^5\)的数据范围,我们肯定会往分块的方向去想(

我们不难想到用分块来维护原数组的值,用\(sum[i][j]\)表示在前\(i\)块中,值为\(j\)的数量。

时间复杂度:修改\(O(logn)\),询问\(O(n)\)会被艹飞

其实此时修改的复杂度已经可以过了,但询问...

我们来康康我们是怎么询问的

我们在询问的时候,预处理了一个统计数组\(total\)\(total[i]\)记录了\([l,r]\)内值为\(i\)的个数,时间复杂度\(O(n)\)

为了让其时间复杂度缩短至\(O(logn)\),我们需要再分一次块,将值域分块。

那么我们就需要两个数组,\(sum1[i][j]\)表示在前\(i\)块中,值域在第\(j\)块的数的数量,\(sum2[i][j]\)同前面的\(sum[i][j]\)

统计数组也开两个(其实一个就行)。

大家看到这里可能会有疑问:我怎么记录每个数的数值呢?

最直观的想法便是直接开一个数组记录下来。然而这样在修改的时候时间复杂度会加一个\(n\),要不起(

怎么消掉这个\(n\)呢?我们有一个办法:每次只修改一个点,将与这个点值相同且在同一个块中的数连在这个点上。

对,就是并查集

我们可以维护一个数组\(rt[i][j]\),表示在第\(i\)块中,数值为\(j\)的我们所定义的这个根的位置(我语文不好不要介意)

然后就是并查集常规操作,\(fa\)数组。

最后我还开了一个\(isrt[i]\)数组,表示第\(i\)个数是否是它所在的块中某个数的根。如果他是\(a\)的根,则\(isrt[i]=a\),否则\(isrt[i]=0\)

P4119 = 分块 + 值域分块 + 并查集

实现:

让我们细化修改和询问操作。

修改:散块(即不完整的块)直接暴力把\(x,y\)的两个并查集拆了重建,整块就直接\(\begin{cases}fa[rt[i][x]]=rt[i][y](rt[i][x]!=0)\\rt[i][x]=rt[i][y](rt[i][x]==0)\end{cases}\)

即可。记得顺带维护一下\(isrt,fa\)就行。

询问没什么好说的,既然要查找第k大,我们就扫一遍所有值域块,如果第k大在这个值域块中,就暴力扫描该值域块。

具体来说,就是先预处理好之前说的\(total\)数组,整块就\(total[i]+=sum1[r\text{所在的块}][i]-sum1[l\text{所在的块}-1][i]\),散块就\(total[isrt[getf(j)]\text{所在的值域块}]++\)就行了。暴力扫块也是依样画葫芦。

需要注意,当块长(不是值域块长)为\(\sqrt{n}\)的时候会TLE翻车,\(O2\)都拉不回来qwq块长取\(500-600\)的时候时间复杂度较优,跑得飞快

我这里预处理了每一个数所在的块和每一个值所在的值域块,其实不要也可qwq

完结撒花花~

code:

#include<bits/stdc++.h>
#define rint register int
using namespace std;
int sum1[180][330] , sum2[180][101000] , rt[180][101000] , fa[101000] , p[101000] , np[101000] , tot;
int n , m , a , opt , l , r , x , y , k , num , siz = 600 , numc = 316 , sizc = 317 , isrt[101000];
int chan[101000] , total[400] , total2[400] , pa , pb;
inline int read()
{
    int a = 0 , f = 0;
    char c = getchar();
    while(c < '0' || c > '9')
    {
        if(c == '-') f = 1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
    {
        a = (a << 1) + (a << 3) + (c ^ 48);
        c = getchar();
    }
    if(f)
    a = -a;
    return a;
}
inline int getf( int k )
{
    if(fa[k] == k)
    return k;
    return fa[k] = getf(fa[k]);
}
void update( int pl , int l , int r , int x , int y )
{
    int maxx = min(n , pl * siz);
    tot = 0;
    for(rint j = (pl - 1) * siz + 1 ; j <= maxx ; j++ )
    {
        if(isrt[getf(j)] == x)
        {
            fa[j] = j;
            isrt[j] = x;
        }
        else if(isrt[getf(j)] == y)
        {
            fa[j] = j;
            isrt[j] = y;
        }
    }
    rt[pl][x] = rt[pl][y] = 0;
    for(rint j = l ; j <= r ; j++ )
    {
        if(isrt[j] == x || isrt[j] == y)
        {
            if(isrt[j] == x)
            tot++;
            if(!rt[pl][y])
            {
                rt[pl][y] = j;
                isrt[j] = y;
            }
            else
            {
                fa[j] = rt[pl][y];
                isrt[j] = 0;
            }
        }
    }
    for(rint j = (pl - 1) * siz + 1 ; j < l ; j++ )
    {
        if(isrt[j] == x)
        {
            if(!rt[pl][x])
            {
                rt[pl][x] = j;
            }
            else
            {
                fa[j] = rt[pl][x];
                isrt[j] = 0;
            }
        }
        else if(isrt[j] == y)
        {
            if(!rt[pl][y])
            {
                rt[pl][y] = j;
            }
            else
            {
                fa[j] = rt[pl][y];
                isrt[j] = 0;
            }
        }
    }
    for(rint j = r + 1 ; j <= maxx ; j++ )
    {
        if(isrt[j] == x)
        {
            if(!rt[pl][x])
            {
                rt[pl][x] = j;
            }
            else
            {
                fa[j] = rt[pl][x];
                isrt[j] = 0;
            }
        }
        else if(isrt[j] == y)
        {
            if(!rt[pl][y])
            {
                rt[pl][y] = j;
            }
            else
            {
                fa[j] = rt[pl][y];
                isrt[j] = 0;
            }
        }
    }
    for(rint j = pl ; j <= num ; j++ )
    {
        sum1[j][np[x]] -= tot;
        sum1[j][np[y]] += tot;
        sum2[j][x] -= tot;
        sum2[j][y] += tot;
    }
}
int main()
{
//  freopen("1.txt" , "r" , stdin);
//  freopen("2.txt" , "w" , stdout);
    n = read() , m = read();
    num = (n - 1) / siz + 1;
    int cnt = 1;
    for(rint i = 1 ; i <= n ; i++ )
    {
        if(i > cnt * siz)
        {
            cnt++;
        }
        p[i] = cnt;
    }
    cnt = 1;
    for(rint i = 1 ; i <= 100000 ; i++ )
    {
        if(i > cnt * sizc)
        {
            cnt++;
        }
        np[i] = cnt;
    }
    for(rint i = 1 ; i <= n ; i++ )
    {
        a = read();
        if(!rt[p[i]][a])
        {
            rt[p[i]][a] = i;
            isrt[i] = a;
            fa[i] = i;
        }
        else
        {
            fa[i] = rt[p[i]][a];
            isrt[i] = 0;
        }
        for(rint j = p[i] ; j <= num ; j++ )
        {
            sum1[j][np[a]]++;
            sum2[j][a]++;
        }
    }
    for(rint i = 1 ; i <= m ; i++ )
    {
        opt = read() , l = read() , r = read();
        pa = p[l] , pb = p[r];
//      cout << opt << " ";
        if(opt == 1)
        {
            x = read() , y = read();
            if(x == y)
            continue;
            if(pa == pb)
            {
                update(pa , l , r , x , y);
            }
            else
            {
                if((pa - 1) * siz + 1 != l)
                {
                    update(pa , l , pa * siz , x , y);
                    pa++;
                }
                if(pb * siz != r)
                {
                    update(pb , (pb - 1) * siz + 1 , r , x , y);
                    pb--;
                }
                chan[pa - 1] = 0; 
                for(rint j = pa ; j <= pb ; j++ )
                {
                    if(rt[j][x])
                    {
                        if(rt[j][y])
                        fa[rt[j][x]] = rt[j][y] , isrt[rt[j][x]] = 0 , rt[j][x] = 0;
                        else
                        rt[j][y] = rt[j][x] , isrt[rt[j][x]] = y , rt[j][x] = 0;
                    }
                    chan[j] = chan[j - 1] + sum2[j][x] - sum2[j - 1][x];
                }
                for(rint j = pa ; j <= pb ; j++ )
                {
                    sum1[j][np[x]] -= chan[j];
                    sum1[j][np[y]] += chan[j];
                    sum2[j][x] -= chan[j];
                    sum2[j][y] += chan[j];
                }
//              cout << chan[pb] << "qwq\n";
                for(rint j = pb + 1 ; j <= num ; j++ )
                {
                    sum1[j][np[x]] -= chan[pb];
                    sum1[j][np[y]] += chan[pb];
                    sum2[j][x] -= chan[pb];
                    sum2[j][y] += chan[pb];
                }
            }
        }
        else
        {
            bool f1 = 0 , f2 = 0;
            k = read();
            memset(total , 0 , sizeof(total));
            memset(total2 , 0 , sizeof(total2));
            if(pa == pb)
            {
                for(rint j = l ; j <= r ; j++ )
                {
                    total[np[isrt[getf(j)]]]++;
                }
            }
            else
            {
                if((pa - 1) * siz + 1 != l)
                {
                    for(rint j = l ; j <= pa * siz ; j++ )
                    {
                        total[np[isrt[getf(j)]]]++;
                    }
                    pa++;
                    f1 = 1;
                }
                if(pb * siz != r)
                {
                    for(rint j = (pb - 1) * siz + 1 ; j <= r ; j++ )
                    {
                        total[np[isrt[getf(j)]]]++;
                    }
                    pb--;
                    f2 = 1;
                }
                for(rint j = 1 ; j <= numc ; j++ )
                {
                    total[j] += sum1[pb][j] - sum1[pa - 1][j];
                }
            }
            for(rint j = 1 ; j <= numc ; j++ )
            {
                if(k <= total[j])
                {
                    int minn = (j - 1) * sizc + 1 , maxx = j * sizc;
                    if(pa == pb)
                    {
                        for(rint i_ = l ; i_ <= r ; i_++ )
                        {
                            if(isrt[getf(i_)] >= minn && isrt[getf(i_)] <= maxx)
                            total2[isrt[getf(i_)] - minn + 1]++;
                        }
                    }
                    else
                    {
                        if(f1)
                        {
                            pa--;
                            for(rint i_ = l ; i_ <= pa * siz ; i_++ )
                            {
                                if(isrt[getf(i_)] >= minn && isrt[getf(i_)] <= maxx)
                                total2[isrt[getf(i_)] - minn + 1]++;
                            }
                            pa++;
                        }
                        if(f2)
                        {
                            pb++;
                            for(rint i_ = (pb - 1) * siz + 1 ; i_ <= r ; i_++ )
                            {
                                if(isrt[getf(i_)] >= minn && isrt[getf(i_)] <= maxx)
                                total2[isrt[getf(i_)] - minn + 1]++;
                            }
                            pb--;
                        }
                        for(rint i_ = minn ; i_ <= maxx ; i_++ )
                        {
                            total2[i_ - minn + 1] += sum2[pb][i_] - sum2[pa - 1][i_];
                        }
                    }
                    for(rint i_ = minn ; i_ <= maxx ; i_++ )
                    {
                        if(k <= total2[i_ - minn + 1])
                        { 
                            printf("%d\n" , i_);
                            break;
                        }
                        else
                        k -= total2[i_ - minn + 1];
                    }
                    break;
                }
                else
                k -= total[j];
            }
        }
    }
    return 0;
}

 

原文:https://www.cnblogs.com/lost-in-tianyi/p/11349599.html

91 发布于 2019-08-14 07:12 评论(0) 阅读(14)
发表评论(最多500字):

推荐博客排行

统计信息

  • 博客[ 6429 ]
  • 评论[ 4 ]
  • 阅读[ 142460 ]