zoukankan      html  css  js  c++  java
  • Luogu P4168 [Violet]蒲公英(分块)

    P4168 [Violet]蒲公英

    题意

    题目背景

    亲爱的哥哥:

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

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

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

    哥哥你要快点回来哦!

    爱你的妹妹 (Violet)

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

    “蒲公英吗……”

    题目描述

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

    为了简化起见,我们把所有的蒲公英看成一个长度为(n)的序列((a_1,a_2cdots 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)mod n + 1,r=(r_0+x-1)mod 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\%)的数据,保证(1le n,mle 3000)

    对于(100\%)的数据,保证(1le nle 40000,1le mle 50000,1le a_ile 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('
    ');
        }
        return 0;
    }
    
  • 相关阅读:
    验证数字范围的小插件
    解决EJB懒加载问题
    JS获取按键的代码,Js如何屏蔽用户的按键,Js获取用户按键对应的ASII码(兼容所有浏览器)
    struts2标签之<s:select>
    c#(winform)中自定义ListItem类方便ComboBox和ListBox添加项完全解决
    辞职前须慎重考虑
    怎样把PDF文件在WinForm窗口中显示出来
    加载报表失败
    经典正则表达式 Javascript
    无法生成项目输出组“内容文件来自...
  • 原文地址:https://www.cnblogs.com/coder-Uranus/p/9884461.html
Copyright © 2011-2022 走看看