• luogu4168蒲公英(区间众数)


    luogu4168蒲公英(区间众数)

    给定n个数,m个区间询问,问每个询问中的众数是什么。

    题面很漂亮,大家可以去看一下。

    对于区间众数,由于区间的答案不能由子区间简单的找出来,所以似乎不能用树形结构。

    用分块的话,设一个区间[x, y],里面包含的最大连续的块的左端点是l,右端点是r。那么显然,这个区间的众数要么是[l, r]的众数,要么是[x, l)和(r, y]中的任意数。所以可以用(f[i][j])表示第i到j块的众数是什么,同时用(s[i][x])表示前i个块中数x的出现次数。这样就可以做到(nsqrt{n})了。我的代码跑的算很快的。因为map没有sort快(废话)。

    有一个很神的地方是f[i][j]只用保存一个值就行了,因为如果在第i个块到第j个块之间,还有数的出现次数和f[i][j]相同,那么它要超越f[i][j],必须满足在一侧的小区间中出现次数比f[i][j]多,所以它至少会在小区间中出现一次。

    如果问题带修怎么办呢?还是可以用分块哟。由于我们要维护f[i][j]和s[i][j],只分(n^{1/3})个块。对于某一个点上的修改,会牵动所有块。再维护cnt[i][j][k]表示i块到j块出现次数为k的数有几个即可。

    #include <cmath>
    #include <cctype>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int maxn=4e4+5, sqrtm=2e2+5, INF=1e9;
    int n, m, barlen, cntbar, a[maxn], bel[maxn], cnt[maxn];
    int t[maxn], cntnum;
    int s[sqrtm][maxn];  //前i个块x的出现次数
    int f[sqrtm][sqrtm];  //第i到j块的众数是什么
    
    void get(int &x){
        x=0; char c; int flag=1;
        for (c=getchar(); !isdigit(c); c=getchar());
        for (x=c-48; c=getchar(), isdigit(c); )
            x=x*10+c-48; x*=flag;
    }
    
    int main(){
        get(n); get(m); barlen=sqrt(n); cntbar=1;
        for (int i=0; i<n; ++i){
            get(a[i]); t[i]=a[i];
            bel[i]=i/barlen;
            if (i&&bel[i]!=bel[i-1]) ++cntbar;
        }
        sort(t, t+n); cntnum=unique(t, t+n)-t;
        for (int i=0; i<n; ++i)
            a[i]=lower_bound(t, t+cntnum, a[i])-t;
        for (int i=0; i<n; ++i) ++s[bel[i]][a[i]];
        for (int i=1; i<cntbar; ++i)  //n^1.5
            for (int j=0; j<n; ++j) s[i][j]+=s[i-1][j];
        int maxm=0, mcnt, q1, q2, l, r, tmp;
        for (int i=0; i<cntbar; ++i){  //n^1.5
            for (int j=0; j<n; ++j) cnt[j]=0;
            mcnt=0;
            for (int j=i*barlen; j<n; ++j){
                ++cnt[a[j]];
                if (cnt[a[j]]>mcnt||(cnt[a[j]]==mcnt&&a[j]<maxm))
                    mcnt=cnt[a[j]], maxm=a[j];
                if (bel[j]!=bel[j+1]) f[i][bel[j]]=maxm;
            }
        }
        maxm=0;
        for (int iq=0; iq<m; ++iq){
            get(q1); get(q2);
            q1=(q1+maxm-1)%n; q2=(q2+maxm-1)%n; mcnt=0;
            if (q1>q2) swap(q1, q2);
            if (bel[q1]==bel[q2]){
                maxm=INF;
                for (int i=q1; i<=q2; ++i) cnt[a[i]]=0;
                for (int i=q1; i<=q2; ++i){
                    ++cnt[a[i]];
                    if (cnt[a[i]]>mcnt||(cnt[a[i]]==mcnt&&a[i]<maxm))
                        mcnt=cnt[a[i]], maxm=a[i];
                }
                printf("%d
    ", maxm=t[maxm]);
                continue;
            }
            l=bel[q1]+1; r=bel[q2]-1;
            if (l<=r) maxm=f[l][r], mcnt=s[r][maxm]-s[l-1][maxm];
            //cnt表示两边需要暴力查找的数的出现个数
            for (int i=q1; i==q1||bel[i]==bel[i-1]; ++i) cnt[a[i]]=0;
            for (int i=q2; i==q2||bel[i]==bel[i+1]; --i) cnt[a[i]]=0;
            for (int i=q1; i==q1||bel[i]==bel[i-1]; ++i) ++cnt[a[i]];
            for (int i=q2; i==q2||bel[i]==bel[i+1]; --i) ++cnt[a[i]];
            for (int i=q1; i==q1||bel[i]==bel[i-1]; ++i){
                tmp=cnt[a[i]]+s[r][a[i]]-s[l-1][a[i]];
                if (tmp>mcnt||(tmp==mcnt&&a[i]<maxm))
                    mcnt=tmp, maxm=a[i];
            }
            for (int i=q2; i==q2||bel[i]==bel[i+1]; --i){
                tmp=cnt[a[i]]+s[r][a[i]]-s[l-1][a[i]];
                if (tmp>mcnt||(tmp==mcnt&&a[i]<maxm))
                    mcnt=tmp, maxm=a[i];
            }
            printf("%d
    ", maxm=t[maxm]);
        }
        return 0;
    }
    
  • 相关阅读:
    【转】算命称骨
    为WinForms程序添加Form级快捷键的最简单方式
    Flex4中Datagrid垂直滚动使combobox&dropdownlist数据消失(已解决)
    Flex & form小技巧
    [转]Android开发之旅:环境搭建及HelloWorld
    [SQL2005]安装Ms SQL Server 2005 开发版时出现性能计数器要求安装错误的解决办法
    [转]Flex 中的皮肤
    天哪,Flex有没有阻塞方式啊?
    [资料库]Flash特效站点(带源码)
    开始学习JAVA
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/8566351.html
Copyright © 2020-2023  润新知