[Violet]蒲公英

题意:

给出一个长度为 \(n\) 序列\(a\) ,\(m\)次询问,每次询问区间 \(l,r\) 里的众数(出现次数最多的数)。若有多个,输出最小的

\(a_i \leq 10^9, n \leq 40000, m \leq 50000\),强制在线。

题解:

看了题解才懂的。根据https://www.cnblogs.com/acfunction/p/10051345.html

hzwer给出了更巧妙的方法http://hzwer.com/3582.html

\(a_i \le 10^9\),所以先离散化。

分块大法好!!!

分析一下:

求出现次数最多的数,那就预处理一下次数,\(s[i][j]\)表示离散化后前\(i\)个块中\(j\)出现了多少次

方法:类似前缀和

然后为了提高效率,再预处理p数组,\(p[i][j]\)表示离散化后第\(i\)个块到第\(j\)个块(最小的)众数

方法:枚举\(i,j\),开个桶,记录每个数出现的次数,然后去出现次数最多的(数值最小的)。

接下来:分类讨论

  1. \(pr\)(\(r\)所在的块)-\(pl\)(\(l\)所在的块)$\geq 2 $

    暴力枚举,和处理\(p\)数组时差不多。

  2. \(pr-pl>2\)

    枚举块外,块内的已经预处理好了。

    \(每个数的出现次数=块内的(已预处理)+块外的(暴力)\)

总的效率为$ O(2n \sqrt n + m \sqrt n) $

要注意细节比较多,QwQ。

代码有点啰嗦,长了点

#include<bits/stdc++.h>
#define Fur(i,x,y) for(int i=x;i<=y;i++)
#define clr(x,y) memset(x,y,sizeof(x))
int MIN(int x,int y){return x<=y?x:y;}
void SWAP(int &x,int &y){x^=y;y^=x;x^=y;}
using namespace std;
#define N 40040
int n,m,L,len,s[220][N],val[N],b[N];
bool vis[N];
struct nod{int id,val,s;}a[N];
struct node{int val,s;}p[220][220];
bool cmp1(nod x,nod y){return x.val<y.val;}
bool cmp2(nod x,nod y){return x.id<y.id;}
void pre(){
    Fur(i,1,len){
        clr(b,0);
        node tmp;tmp.val=tmp.s=0;
        Fur(j,i,len){
            Fur(k,(j-1)*L+1,MIN(j*L,n)){
                b[a[k].s]++;
                if(b[a[k].s]>tmp.s)tmp.val=a[k].s,tmp.s=b[a[k].s];
                else if(b[a[k].s]==tmp.s)tmp.val=MIN(tmp.val,a[k].s);
            }
            p[i][j]=tmp;
        }
    }
    Fur(i,1,len){
        Fur(j,1,n)s[i][a[j].s]=s[i-1][a[j].s];
        Fur(j,(i-1)*L+1,MIN(n,i*L))s[i][a[j].s]++;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;L=sqrt(n);len=(n+L-1)/L;
    Fur(i,1,n)cin>>a[i].val,a[i].id=i;
    sort(a+1,a+n+1,cmp1);a[0].val=-1;
    Fur(i,1,n){
        a[i].s=a[i-1].s+(a[i-1].val!=a[i].val);
        val[a[i].s]=a[i].val;
    }
    sort(a+1,a+n+1,cmp2);
    pre();
    int l,r,la=0,ans=0;
    Fur(i,1,m){
        cin>>l>>r;
        l=(l+la-1)%n+1;r=(r+la-1)%n+1;
        if(l>r)SWAP(l,r);
        int pl=(l-1)/L+1,pr=(r-1)/L+1;
        if(pr-pl<=2){
            ans=0;
            Fur(j,l,r)b[a[j].s]=0;
            Fur(j,l,r){
                b[a[j].s]++;
                if(b[a[j].s]>b[ans])ans=a[j].s;
                else if(b[a[j].s]==b[ans])ans=MIN(ans,a[j].s);
            }
        }
        else{
            ans=p[pl+1][pr-1].val;
            b[ans]=vis[ans]=0;
            Fur(j,l,MIN(n,pl*L))b[a[j].s]=vis[a[j].s]=0;
            Fur(j,(pr-1)*L+1,r)b[a[j].s]=vis[a[j].s]=0;
            Fur(j,l,MIN(n,pl*L))b[a[j].s]++;
            Fur(j,(pr-1)*L+1,r)b[a[j].s]++;
            int mx=0,mxv;
            Fur(j,l,MIN(n,pl*L))if(!vis[a[j].s]){
                vis[a[j].s]=1;
                int tmp=b[a[j].s]+s[pr-1][a[j].s]-s[pl][a[j].s];
                if(mx<tmp)mx=tmp,mxv=a[j].s;
                else if(mx==tmp)mxv=MIN(mxv,a[j].s);
            }
            Fur(j,(pr-1)*L+1,r)if(!vis[a[j].s]){
                vis[a[j].s]=1;
                int tmp=b[a[j].s]+s[pr-1][a[j].s]-s[pl][a[j].s];
                if(mx<tmp)mx=tmp,mxv=a[j].s;
                else if(mx==tmp)mxv=MIN(mxv,a[j].s);
            }
            if(mx>b[ans]+p[pl+1][pr-1].s)ans=mxv;
            else if(mx==b[ans]+p[pl+1][pr-1].s)ans=MIN(ans,mxv);
        }
        cout<<(la=val[ans])<<"\n";
    }
}

原文地址:https://www.cnblogs.com/mimiorz/p/10321774.html

时间: 2024-10-09 15:06:30

[Violet]蒲公英的相关文章

Luogu P4168 [Violet]蒲公英

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

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

题面 洛咕 Solution 题目要求求出区间众数,强制在线. 区间众数是一个比较尴尬的问题,我们无法用区间数据结构来处理这个问题,因为我们没法很好的合并区间众数的答案. 既然区间数据结构解决不了这个问题,我们可以考虑一下使用基于分块的算法,例如莫队. 这题用莫队非常好处理,不幸的是,这题要求强制在线. 因此我们考虑使用分块算法. 分块算法的核心在于把一整个块的信息压缩起来以便快速处理. 我们要查询一段区间的众数,我们可以考虑这样搞:对于这个区间内连续的块,我们先快速地查询这个连续的块中的众数,

[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

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

题解【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

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]$,更新答案. 当块长

[violet]蒲公英题解

前几天刚学习了分块,感觉这道题用分块求解的方式挺巧妙的 既然用的是分块,那么肯定是两端暴力求解,中间要快速地处理每个块 首先我们要得到一个结论,最后求出的这一个众数必定为中间块的众数或者是两端的任意一个数,那么我们用\(nu[i][j]\)来表示第\(i\)个块到第\(j\)个块的众数,我们可以用用\(O(n\sqrt{n})\)的时间复杂度,先枚举每个块,然后枚举后面的点来求出\(nu[i][j]\),放一下这一段的代码 void pre(int x) { memset(cnt,0,sizeo

分块简单入门

分块简单入门 按 树状数组虽超级快,但是很僵硬,不灵活:线段树虽快,但是却不直观,代码量大:所以,速度较慢但直观形象.代码量小的分块大法不实为线段树的替代品. 网络上关于分块的教程不知道为什么很少,虽然早有hzwer大神的分块九讲,但是还是少了入门级详解教程.此篇将分为三个阶段,保证初学者在有意识地阅读后基本掌握分块. 1.简单的入门题 问题引入 给定长度为N(N<=1e5)的正数数列A,然后输入Q(Q<=1e5)行操作命令,指令形如Q l r,表示统计数列中第l~r个数中的最大值 思路 首先

BZOJ2724: [Violet 6]蒲公英

2724: [Violet 6]蒲公英 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 795  Solved: 248[Submit][Status] Description Input 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1 Output Sample Input Sample Output HINT 修正下: n <= 40000, m <= 50000 S