Luogu P4168 [Violet]蒲公英

P4168 [Violet]蒲公英

题意

题目背景

亲爱的哥哥:

你在那个城市里面过得好吗?

我在家里面最近很开心呢。昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多小朋友也被它杀掉了。我觉得把那么可怕的怪物召唤出来的那个坏蛋也很坏呢。不过奶奶说他是很难受的时候才做出这样的事的……

最近村子里长出了一大片一大片的蒲公英。一刮风,这些蒲公英就能飘到好远的地方了呢。我觉得要是它们能飘到那个城市里面,让哥哥看看就好了呢!

哥哥你要快点回来哦!

爱你的妹妹 \(Violet\)

\(Azure\) 读完这封信之后微笑了一下。

“蒲公英吗……”

题目描述

在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。

为了简化起见,我们把所有的蒲公英看成一个长度为\(n\)的序列\((a_1,a_2\cdots a_n)\),其中\(a_i\)为一个正整数,表示第\(i\)棵蒲公英的种类编号。

而每次询问一个区间\([l,r]\),你需要回答区间里出现次数最多的是哪种蒲公英,如果有若干种蒲公英出现次数相同,则输出种类编号最小的那个。

注意,你的算法必须是在线的

输入输出格式

输入格式:

第一行两个整数\(n,m\),表示有\(n\)株蒲公英,\(m\)次询问。

接下来一行\(n\)个空格分隔的整数\(a_i\),表示蒲公英的种类

再接下来\(m\)行每行两个整数\(l_0,r_0\),我们令上次询问的结果为\(x\)(如果这是第一次询问,则\(x=0\))。

令\(l=(l_0+x-1)\bmod n + 1,r=(r_0+x-1)\bmod n + 1\),如果\(l>r\),则交换\(l,r\)。

最终的询问区间为[l,r]。

输出格式:

输出\(m\)行。每行一个整数,表示每次询问的结果。

输入输出样例

输入样例#1:

6 3
1 2 3 2 1 2
1 5
3 6
1 5

输出样例#1:

1
2
1

说明

对于\(20\%\)的数据,保证\(1\le n,m\le 3000\)。

对于\(100\%\)的数据,保证\(1\le n\le 40000,1\le m\le 50000,1\le a_i\le 10^9\)。

思路

\(600\ AC\)!整理博客。 --Uranus

重新拾起分块这个毒瘤东西,并在\(alecli\)大佬的推荐下做了这道题。

我们把序列分成块,离散化之后记录每个数字在各个块内出现的次数,那么出现最多的就是众数了。这里我用数组\(s[i][j][k]\)表示第\(i\)块到第\(j\)块数字\(k\)的出现次数。然后我们统计区间众数,用\(md[i][j]\)记录众数大小,\(tms[i][j]\)记录众数出现次数。

预处理到这里就结束了,接下来考虑查询操作。对于每次查询的区间,一定是由一个大块和这个大块左边的一些数,以及右边的一些数组成的,那么显然,这个区间最后的众数为大块的众数,或者大块左边出现的数,或者大块右边出现的数。

那么我们就直接用之前统计出的大块的三个数组,在这个基础上把左右两小块用预处理时的相同方法加进来,统计答案后再把小块去掉,就可以很方便的查询并保持预处理数组了。

既然是分块,那么细节一定是很多的,具体来说有这几条:

  • 注意离散化,注意常数优化。
  • 如果查询区间不包含大块,直接把两个小块暴力处理。
  • \(alecli\)说分块的大小会影响时间复杂度,并告诉我块的大小最好为\(n^\frac{2}{3}\),虽然并不知道为什么。

只要耐心调试,最终一定能\(AC\)的,祝你好运!

AC代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=4e4+5,MAXM=45;
int n,m,len,ans,cnt,a[MAXN],b[MAXN],c[MAXN];
int sz,num,l[MAXM],r[MAXM],belong[MAXN],s[MAXM][MAXM][MAXN],md[MAXM][MAXM],tms[MAXM][MAXM];
int read()
{
    int re=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
void print(int x,bool first)
{
    if(!x)
    {
        if(first) putchar('0');
        return ;
    }
    print(x/10,false);
    putchar('0'+x%10);
}
void prework()
{
    num=pow(double(n),1.0/3.0),sz=n/num;
    for(int i=1;i<=num;i++) l[i]=r[i-1]+1,r[i]=sz*i;
    if(r[num]<n) num++,l[num]=r[num-1]+1,r[num]=n;
    for(int i=1;i<=num;i++)
        for(int j=l[i];j<=r[i];j++)
            belong[j]=i;
    sort(b+1,b+n+1);
    len=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++) c[i]=lower_bound(b+1,b+len+1,a[i])-b;
    for(int i=1;i<=num;i++)
        for(int j=i;j<=num;j++)
        {
            for(int k=l[i];k<=r[j];k++) s[i][j][c[k]]++;
            for(int k=1;k<=len;k++)
                if(tms[i][j]<s[i][j][k]||(tms[i][j]==s[i][j][k]&&k<md[i][j]))
                    md[i][j]=k,tms[i][j]=s[i][j][k];
        }
}
void ask(int ll,int rr)
{
    if(ll>rr) swap(ll,rr);
    int lbe=belong[ll],rbe=belong[rr],L,R;ans=cnt=0;
    if(rbe-lbe<=2) L=R=0;
    else L=lbe+1,R=rbe-1;
    if(L==R)
    {
        for(int i=ll;i<=rr;i++)
        {
            s[L][R][c[i]]++;
            if(cnt<s[L][R][c[i]]||(cnt==s[L][R][c[i]]&&ans>c[i]))
                cnt=s[L][R][c[i]],ans=c[i];
        }
        for(int i=ll;i<=rr;i++) s[L][R][c[i]]--;
    }
    else
    {
        ans=md[L][R],cnt=tms[L][R];
        for(int i=ll;i<=r[lbe];i++)
        {
            s[L][R][c[i]]++;
            if(cnt<s[L][R][c[i]]||(cnt==s[L][R][c[i]]&&ans>c[i]))
                cnt=s[L][R][c[i]],ans=c[i];
        }
        for(int i=l[rbe];i<=rr;i++)
        {
            s[L][R][c[i]]++;
            if(cnt<s[L][R][c[i]]||(cnt==s[L][R][c[i]]&&ans>c[i]))
                cnt=s[L][R][c[i]],ans=c[i];
        }
        for(int i=ll;i<=r[lbe];i++) s[L][R][c[i]]--;
        for(int i=l[rbe];i<=rr;i++) s[L][R][c[i]]--;
    }
    ans=b[ans];
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++) a[i]=b[i]=read();
    prework();
    while(m--)
    {
        int x=(read()+ans-1)%n+1,y=(read()+ans-1)%n+1;
        ask(x,y);
        print(ans,true);putchar('\n');
    }
    return 0;
}

原文地址:https://www.cnblogs.com/coder-Uranus/p/9884461.html

时间: 2024-10-06 15:23:42

Luogu P4168 [Violet]蒲公英的相关文章

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

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

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

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

luogu P1445 [Violet]嘤F♂A

博主决定更博文啦 这道题一开始没什么思路啊qwq 要求 \(\frac{1}{x}+\frac{1}{y}=\frac{1}{n!}\) 的正整数解总数 首先通分,得 \[\frac{x+y}{xy}=\frac{1}{n!}\] 然后移项,得 \[n!(x+y)=xy\] ↑止步于此↑ \[n!(x+y)-xy=0\] 这里令\(y=n!+k(k\in N^*)\),因为由原方程得出\(y\)是大于\(n!\)的 原方程变为 \[n!(x+(n!+k))-x(n!+k)=0\] \[(n!)^

[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

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

[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

Luogu P4169 [Violet]天使玩偶/SJY摆棋子

传送门 二维平面修改+查询,cdq分治可以解决. 求关于某个点曼哈顿距离(x,y坐标)最近的点——dis(A,B) = |Ax-Bx|+|Ay-By| 但是如何去掉绝对值呢? 查看题解发现假设所有的点都在查询点的左下方,dis(A,B) = (Ax-Bx)+(Ay-By) = (Ax+Ay)-(Bx+By) 只要求满足Bx<Ax,By<Ay且Bx,By之和最大的点就好了. 那么如何把所有的点转化到该查询的左下呢? 对于每个查询,可以把一.二.四象限的点都通过对称转移到第三象限.但查询很多,不可

[violet]蒲公英题解

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