• Luogu4168 蒲公英 (分块)


    题目传送门

    题意

    长度为n的序列,有m次询问,每次询问求([l,r]) 间的众数,如果有多个,输出最小的那个
    (nle 4 imes 10^4,mle 5 imes 10^5,a_ile10^9)

    分析

    题目中要求在线(询问用上次答案加密)。众数不具有“区间可加性”,所以需要分块。
    假设分成(T)块,每块长度(L=N/T)。每次询问([l,r]),设(l)处在(p)块,(r)处在(q)块,则区间分为三部分。

    • 开头的([l,L))
    • (第p+1sim q-1块构成的区间[L,R])
    • 结尾的((R,r])
      显然序列在整个询问区间的众数只可能出现在([L,R])中的众数,或者头尾出现的数字中。
      可以预处理所有分块区间的众数(d[l][r])表示(l)块到(r)块中的众数。复杂度(O(TN))
      对于头尾的部分,暴力枚举每个数字,通过预先存在每个数字出现的位置,这样就可以二分查询该数字([l,r])出现的次数,然后选取最大的即可。复杂度(NM/T*log(N))
      所以总复杂度为(O(TN+NM/T*log(N))),又N和M几乎同级,可得(T = sqrt{Mlog(N)})时较优。
      更多细节请看代码

    思路&步骤

    1. 存下每个数字,离散化,处理每个数字出现的位置,储存在STL::vector里面
    2. 分块,预处理整区间的众数
    3. 查询,先取整个区间的答案,然后两头扫
    #include <bits/stdc++.h>
    using namespace std;
    const int N = 40010;
    const int M = 500010;
    /*
    be[i] 表示 i 在第几块
    id[i] 表示a[i]在离散化数组中的位置
    blc为块大小
    cnt[i] 预处理时需要的数组,表示 i 的个数
    d[i][j]表示 i块与 j块之间的众数
    v[i] 表示 数字 i 出现的位置
    g为离散化vector
    */
    int be[N],id[N],a[N],n,m;
    int blc,cnt[N],d[1200][1200];
    vector<int> v[N],g;
    
    int getID(int x){
        return lower_bound(g.begin(),g.end(),x) - g.begin();
    }
    //预处理整块区间众数
    void build(int x){
        int res = -1;
        for(int i=0;i<g.size();i++)cnt[i] = 0;
        for(int i=(x-1)*blc+1;i<=n;i++){
            cnt[id[i]] ++;
            if(res == -1 || cnt[id[i]] > cnt[res] || (cnt[id[i]] == cnt[res] && id[i] < res)){
                res = id[i];
            }
            d[x][be[i]] = res;
        }
    }
    //询问[l,r]间x的个数
    int ask(int l,int r,int x){
        return upper_bound(v[x].begin(),v[x].end(),r) - lower_bound(v[x].begin(),v[x].end(),l);
    }
    int query(int l,int r){
    	//mxcnt为当前众数的个数,res为答案
        int mxcnt = 0,res = -1;
        if(be[r] - be[l] > 1){
            res = d[be[l]+1][be[r]-1];
            mxcnt = ask(l,r,res);
        }
        if(be[r] == be[l]){
            for(int i=l;i<=r;i++){
                int cnt = ask(l,r,id[i]);
                if(res == -1 || mxcnt < cnt || (mxcnt == cnt && id[i] < res)){
                    res = id[i];
                    mxcnt = cnt;
                }
            }
        }
        else{
            for(int i=l;i<=be[l] * blc;i++){
                int cnt = ask(l,r,id[i]);
                if(res == -1 || mxcnt < cnt || (mxcnt == cnt && id[i] < res)){
                    res = id[i];
                    mxcnt = cnt;
                }
            }
            for(int i=(be[r]-1) * blc;i<=r;i++){
                int cnt = ask(l,r,id[i]);
                if(res == -1 || mxcnt < cnt || (mxcnt == cnt && id[i] < res)){
                    res = id[i];
                    mxcnt = cnt;
                }
            }
        }
        return res;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            g.push_back(a[i]);
        }
        sort(g.begin(),g.end());g.erase(unique(g.begin(),g.end()),g.end());
        for(int i=1;i<=n;i++){
            id[i] = getID(a[i]);
            v[id[i]].push_back(i);
        }
        blc = max(1,(int)(n/sqrt(m * log2(n))));
        for(int i=1;i<=n;i++){
            be[i] = (i-1) / blc + 1;
        }
        for(int i=1;i<=be[n];i++)
            build(i);
        int res = 0;
        while(m--){
            int l,r;
            scanf("%d%d",&l,&r);
            l = (l + res - 1) % n + 1;
            r = (r + res - 1) % n + 1;
            if(l > r)swap(l,r);
            res = g[query(l,r)];
            printf("%d
    ",res);
        }
        return 0;
    }
    
  • 相关阅读:
    Delphi xe8 分割字符串太方便了
    Delphi XE8 如何删除Castalia这个渣渣
    Delphi XE7 XE8 开发iphone运用
    美丽图标网站
    xe6输出枚举类型每项字符串
    Delphi XE6记录类型赋值
    XE6 ShortString与String相互转换
    关于Delphi XE6的学习
    Delphi Byte数组与String类型的转换
    Delphi XE6 TStringHelper中的string类型转换
  • 原文地址:https://www.cnblogs.com/1625--H/p/11309888.html
Copyright © 2020-2023  润新知