Loj#6285.数列分块入门-9-暴力分块

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

题目:传送门

 给出一个长为 n 的数列,以及 n 个询问,询问区间[L, R]的最小众数。

思路:

1.分块暴力搞:

离散化数据
预处理出每两个块间的众数f[i][j]
预处理每个数出现的位置,放在一个桶里面。
预处理前k块中第j个数据的出现次数 cnt[k][j]// 其实这个预处理可以不用,也可以每次upper_bound()-lower_bound()代替

一:对于整块区间直接O(1)查询众数f[belong[a]+1][belong[b]-1],O(1)查询数量cnt[belong[b]-1][x]-cnt[belong[a]][x]
二:对于旁边的区间的数据,枚举在查询区间[a, b]出现的次数。如果次数较大或者数据较小就更新ans。

(频繁用memset清空1e5的数组导致跑了6000+ms,改成枚举指定数量快了10倍,577ms左右)

AC代码;

//577ms
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<ctime>
#include<vector>
#include<cmath>
#define fuck(x) cout<<"* "<<x<<"\n"
using namespace std;
typedef long long LL;
const int N = 1e5+7;
const int INF = 0x3f3f3f3f;
int n,belong[N],br[N],now[N],cnt[N],f[500][500],l[N],r[N],block,Num,k;
vector<int> g[N];
int ar[N],cw[500][N];
inline void build(){
    block=sqrt(n*1.0);
    Num=n/block;if(n%block)Num++;
    for(int i=1;i<=Num;++i){
        l[i]=(i-1)*block+1;r[i]=i*block;
    }
    r[Num]=n;
    for(int i=1;i<=n;++i){
        belong[i]=(i-1)/block+1;
        now[i]=lower_bound(br+1,br+1+k,ar[i])-br;
        cw[belong[i]][now[i]]++;
    }
    for(int i=1;i<=n;++i){
        g[now[i]].push_back(i);
    }
    for(int i=1;i<=Num;++i){
        for(int j=1;j<=k;++j){
            cw[i][j]+=cw[i-1][j];
        }
    }
}
inline int get(int a,int b,int x){
    if(belong[a]==belong[b]){
        return cw[belong[b]][x]-cw[belong[a]][x];
    }
    return cw[belong[b]-1][x]-cw[belong[a]][x];
    //return (upper_bound(g[x].begin(),g[x].end(),b)-lower_bound(g[x].begin(),g[x].end(),a));
}
inline void chaobaoli(int a,int b){
    for(int i=0;i<=k;++i){
        cnt[i]=0;
    }
    int p=f[belong[a]+1][belong[b]-1];
    int num_max=get(a,b,p);
    int da=min(r[belong[a]],b);
    for(int i=a;i<=da;++i){
        cnt[now[i]]++;
        int tnum=get(a,b,now[i])+cnt[now[i]];
        if(tnum>num_max||(tnum==num_max&&br[p]>br[now[i]])){
            p=now[i];
            num_max=tnum;
        }
    }
    if(belong[a]!=belong[b])
    for(int i=l[belong[b]];i<=b;++i){
        cnt[now[i]]++;
        int tnum=get(a,b,now[i])+cnt[now[i]];
        if(tnum>num_max||(tnum==num_max&&br[p]>br[now[i]])){
            p=now[i];
            num_max=tnum;
        }
    }
    printf("%d\n",br[p] );
}
int main(){
    scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%d",&ar[i]);
            br[i]=ar[i];
        }
        sort(br+1,br+1+n);
        k=1;
        for(int i=2;i<=n;++i){
            if(br[i]!=br[i-1]){
                br[++k]=br[i];
            }
        }
        build();
        for(int i=1;i<=Num;++i){
            for(int i=0;i<=k;++i){
                cnt[i]=0;
            }
            int num_max=0,p=0;
            for(int j=l[i];j<=n;++j){
                cnt[now[j]]++;
                if(cnt[now[j]]>num_max||(cnt[now[j]]==num_max&&br[p]>br[now[j]])){
                    p=now[j];num_max=cnt[now[j]];
                }
                f[i][belong[j]]=p;
            }
        }
        for(int i=0,a,b;i<n;++i){
            scanf("%d%d",&a,&b);
            if(a>b)a^=b^=a^=b;
            chaobaoli(a,b);
        }
    return 0;
}


题目描述

给出一个长为 n 的数列,以及 n 个操作,操作涉及询问区间的最小众数。
输入格式
第一行输入一个数字 n。

第二行输入 n 个数字,第 i 个数字为 ai,以空格隔开。

接下来输入 n 行询问,每行输入两个数字 l、r,以空格隔开。

表示查询位于 [l,r]的数字的众数。
输出格式
对于每次询问,输出一行一个数字表示答案。
样例
样例输入
4
1 2 2 4
1 2
1 4
2 4
3 4
样例输出
1
2
2
2
输入:
10
1 2 3 4 3 4 5 2 3 4
1 4
2 6
9 10
4 8
1 8
2 10
4 7
3 9
4 8
6 8
输出:
1
3
3
4
2
3
4
3
4
2

猜你喜欢

转载自blog.csdn.net/qq_39599067/article/details/80710354
今日推荐