luogu4168蒲公英(区间众数)

luogu4168蒲公英(区间众数)

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

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

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

用分块的话,设一个区间[x, y],里面包含的最大连续的块的左端点是l,右端点是r。那么显然,这个区间的众数要么是[l, r]的众数,要么是[x, l)和(r, y]中的任意数。所以可以用\(f[i][j]\)表示第i到j块的众数是什么,同时用\(s[i][x]\)表示前i个块中数x的出现次数。这样就可以做到\(n\sqrt{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\n", 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\n", maxm=t[maxm]);
    }
    return 0;
}
posted @ 2018-03-14 11:27  pechpo  阅读(302)  评论(0编辑  收藏  举报