[Luogu P4168] [Violet]蒲公英 (分块)

题面

洛咕


Solution

题目要求求出区间众数,强制在线。

区间众数是一个比较尴尬的问题,我们无法用区间数据结构来处理这个问题,因为我们没法很好的合并区间众数的答案。

既然区间数据结构解决不了这个问题,我们可以考虑一下使用基于分块的算法,例如莫队。
这题用莫队非常好处理,不幸的是,这题要求强制在线。
因此我们考虑使用分块算法。

分块算法的核心在于把一整个块的信息压缩起来以便快速处理。
我们要查询一段区间的众数,我们可以考虑这样搞:对于这个区间内连续的块,我们先快速地查询这个连续的块中的众数,然后我们暴力处理这个区间剩余的左右两个零散的点,开一个桶暴力维护这些零散的点每个颜色出现的次数,每新加入一个点,就与整个区间的答案比较一下,如果更优就替换答案。

为了实现上面那个思路,我们必须要实现两点:快速求出一段连续块的每个颜色出现的次数,快速求出一段连续块的众数。
对于第一个问题,解决方法很简单,我们暴力做前缀和即可,复杂度\(O(n*\sqrt n)\)

for(int i=1;i<=cnt_block;i++)
    {
        for(int j=1;j<=to;j++)
            pre[i][j]=pre[i-1][j];
        for(int j=(i-1)*size;j<i*size;j++)
            pre[i][a[j]]++;
    }

对于第二个问题,我们可以考虑把每段连续块的答案预处理出来。
具体做法是:我们枚举每个块,然后我们暴力往后扫描,每扫到一个块的结尾就记录答案。

cnt[0]=-0x3f3f3f3f;
    for(int i=1;i<=cnt_block;i++)
    {
        int t_ans=0;
        for(int j=(i-1)*size;j<=n;j++)
        {
            cnt[a[j]]++;
            if(cnt[a[j]]>cnt[t_ans] or (cnt[a[j]]==cnt[t_ans] and a[j]<t_ans))
                t_ans=a[j];
            if((j+1)%size==0)
                f[i][j/size+1]=t_ans;
        }
        memset(cnt,0,sizeof cnt);
        cnt[0]=-0x3f3f3f3f;
    }

就酱,这题就被我们搞定啦~
复杂度\(O(n\sqrt n)\)


Code

本题实现上有较多细节要处理,请小心

//Luogu P4168 [Violet]蒲公英
//Feb,4th,2019
//分块套路题
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
long long read()
{
    long long x=0,f=1; char c=getchar();
    while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
const int N=40000+1000;
const int M=200+10;
int n,m,a[N],b[N],t_n,mmap[N];//离散值->原值
int f[M][M],pre[M][N];//f[i][j]:块i到j的众数,pre[i][j]:到i块为止,颜色j的出现次数前缀和
int cnt[N];//零时记录每个元素出现次数
int main()
{
    //freopen("testdata.in","r",stdin);
    //freopen("4168.out","w",stdout);

    t_n=n=read(),m=read();
    for(int i=1;i<=n;i++)
        a[i]=read(),b[i]=a[i];

    sort(b+1,b+1+n);
    int to=0,last=0;
    for(int i=1;i<=n;i++)
        if(b[i]!=last)
            last=b[i],b[++to]=b[i];
    for(int i=1;i<=n;i++)
    {
        int t=lower_bound(b+1,b+1+to,a[i])-b;
        mmap[t]=a[i],a[i]=t;
    }
    int size=int(sqrt(n)),cnt_block=n/size+1;
    n=cnt_block*size-1;
    for(int i=1;i<=cnt_block;i++)
    {
        for(int j=1;j<=to;j++)
            pre[i][j]=pre[i-1][j];
        for(int j=(i-1)*size;j<i*size;j++)
            pre[i][a[j]]++;
    }
    cnt[0]=-0x3f3f3f3f;
    for(int i=1;i<=cnt_block;i++)
    {
        int t_ans=0;
        for(int j=(i-1)*size;j<=n;j++)
        {
            cnt[a[j]]++;
            if(cnt[a[j]]>cnt[t_ans] or (cnt[a[j]]==cnt[t_ans] and a[j]<t_ans))
                t_ans=a[j];
            if((j+1)%size==0)
                f[i][j/size+1]=t_ans;
        }
        memset(cnt,0,sizeof cnt);
        cnt[0]=-0x3f3f3f3f;
    }

    int ans=0;
    for(int i=1;i<=m;i++)
    {
        int l=read(),r=read();
        l=(l+ans-1)%t_n+1,r=(r+ans-1)%t_n+1;
        if(l>r) swap(l,r);

        int bl=l/size+1,br=r/size+1;
        ans=0;
        if(bl+1<=br-1)
            ans=f[bl+1][br-1];
        for(int j=l;j<bl*size and j<=r;j++)
        {
            cnt[a[j]]++;
            int tmp1=cnt[a[j]],tmp2=cnt[ans];
            if(bl+1<=br-1)
                tmp1+=pre[br-1][a[j]]-pre[bl][a[j]],
                tmp2+=pre[br-1][ans]-pre[bl][ans];
            if(tmp1>tmp2 or (tmp1==tmp2 and a[j]<ans))
                ans=a[j];
        }
        if(bl!=br)
            for(int j=(br-1)*size;j<=r;j++)
            {
                cnt[a[j]]++;
                int tmp1=cnt[a[j]],tmp2=cnt[ans];
                if(bl+1<=br-1)
                    tmp1+=pre[br-1][a[j]]-pre[bl][a[j]],
                    tmp2+=pre[br-1][ans]-pre[bl][ans];
                if(tmp1>tmp2 or (tmp1==tmp2 and a[j]<ans))
                    ans=a[j];
            }

        for(int j=l;j<bl*size and j<=r;j++)
            cnt[a[j]]--;
        if(bl!=br)
            for(int j=(br-1)*size;j<=r;j++)
                cnt[a[j]]--;
        cnt[0]=-0x3f3f3f3f;

        ans=mmap[ans];
        printf("%d\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/GoldenPotato/p/10351638.html

时间: 2024-07-31 06:14:17

[Luogu P4168] [Violet]蒲公英 (分块)的相关文章

Luogu P4168 [Violet]蒲公英

P4168 [Violet]蒲公英 题意 题目背景 亲爱的哥哥: 你在那个城市里面过得好吗? 我在家里面最近很开心呢.昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多小朋友也被它杀掉了.我觉得把那么可怕的怪物召唤出来的那个坏蛋也很坏呢.不过奶奶说他是很难受的时候才做出这样的事的-- 最近村子里长出了一大片一大片的蒲公英.一刮风,这些蒲公英就能飘到好远的地方了呢.我觉得要是它们能飘到那个城市里面,让哥哥看看就好了呢! 哥哥你要快点回来哦! 爱你的妹妹 \(V

P4168 [Violet]蒲公英 分块

这道题算是好好写了.写了三种方法. 有一个好像是$qwq$$N\sqrt(N)$的方法,,但是恳请大佬们帮我看看为什么这么慢$qwq$(后面的第三种) 注:$pos[i]$表示$i$属于第$pos[i]$块. 第一种是统计所有可能的块组成的区间中(第i块到第j块),每个数出现的次数,记做$f[i][j][k]$,和所有可能的块组成的区间的答案,记做$h[i][j]$. 然后每次先把整块的答案作为初始答案,然后对于散块中的每个值$vl$,暴力修改对应的$f[i][j][vl]$,更新答案. 当块长

p4168 [Violet]蒲公英(分块)

区间众数的重题 和数列分块入门9双倍经验还是挺好的 然后开O2水过 好像有不带log的写法啊 之后在补就是咕咕咕 // luogu-judger-enable-o2 #include <cstdio> #include <algorithm> #include <cstring> #include <vector> #include <map> #include <cmath> using namespace std; int m,b

[Violet]蒲公英 分块

Code: #include<cstdio> #include<string> #include<iostream> #include<algorithm> #include<vector> #include<cstring> #include<cmath> using namespace std; void setIO(string a){ freopen((a+".in").c_str(),&quo

【BZOJ2724】[Violet 6]蒲公英 分块+二分

[BZOJ2724][Violet 6]蒲公英 Description Input 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1 Output Sample Input 6 3 1 2 3 2 1 2 1 5 3 6 1 5 Sample Output 1 2 1 HINT 修正下: n <= 40000, m <= 50000 题解:分块还是练脑子啊~ 结论:一个区间的众数要么是区间中一个块的众数,要么是块外的任意

【bzoj2724】[Violet 6]蒲公英 分块+STL-vector

题目描述 输入 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1 输出 样例输入 6 3 1 2 3 2 1 2 1 5 3 6 1 5 样例输出 1 2 1 题解 分块+STL-vector 一个显而易见的结论:区间众数一定是一段连续的块的众数或块外的数,证明略(逃 先把数据离散化,然后分块预处理出f[i][j],表示从块i到块j的众数位置.具体实现的话直接开个桶存一下就好了. 然后考虑询问,整块的直接拿出来求一下出现次

[日常摸鱼]bzoj2724蒲公英-分块

区间众数经典题~ http://begin.lydsy.com/JudgeOnline/problem.php?id=4839这里可以提交~ 题意大概就是没有修改的询问区间众数,如果有一样的输出最小的,强制在线,$n \leq 4*10^4,a_i \leq 10^9$. log数据结构脑补一遍好像没什么可以做的,数据范围我们可以分块! 不过分块之前肯定要离散化一下,而且还要保存离散化前的数据(因为要回答的是出现最多的数),离散化的方法在上一篇博客里面~ 假设分成$L$块,每块大小$s=\lfl

luogu P2617 Dynamic Rankings(分块,n &lt;= 1e4)

嘟嘟嘟 带修改区间第k大. 然而某谷把数据扩大到了1e5,所以用分块现在只能得50分. 分块怎么做呢?很暴力的. 基本思想还是块内有序,块外暴力统计. 对于修改,直接重排修改的数所在块,时间复杂度O(√nlogn√n). 对于询问,二分答案,然后在每个块内再二分统计小于mid的数有几个,块外暴力统计,时间复杂度O(m * log1e9 * √nlog√n),所以只能过1e4. 1 #include<cstdio> 2 #include<iostream> 3 #include<

题解【luogu4168 [Violet]蒲公英】

Description 给出一个长度为 \(n\) 序列 \(a\) ,\(m\) 次询问,每次询问区间 \([l,r]\) 里的众数(出现次数最多的数).若有多个,输出最小的. \(a_i \leq 10^9, n \leq 40000, m \leq 50000\),强制在线. Solution \(a_i \leq 10^9\) ,先离散化.然后 算法一:暴力 \(n ^ 2\) ,预计得分 20 : 实际得分 20 (开了 O2 直接变成 85 啥操作) 算法二:\(n \leq 400