首页 > 其他分享 >洛谷P4168 蒲公英 分块处理区间众数模板

洛谷P4168 蒲公英 分块处理区间众数模板

时间:2022-11-12 17:44:59浏览次数:78  
标签:洛谷 分块 int get tim 众数 mx P4168

题面

许久以前我还不怎么去机房的时候,一位大佬好像一直在做这道题,他称这道题目为“大分块”。

其实这道题目的思想不只可以用于处理区间众数,还可以处理很多区间数值相关问题。

让我们在线处理区间众数。

数据范围1e5,考虑分块。

先对蒲公英种类离散化。

预处理

预处理出两个数组。

一个数组sum[ i ][ j ]表示第j种颜色到第i个分块的前缀和。

另一个数组 zhongshu[ i ][ j ]表示第i个分块到第j个分块这个区间内的众数。

维护这两个操作时间复杂度都不能超过n3/2

第一个数组很好维护,输入的时候将输入位置对应分块的相应种类加一,然后跑一遍前缀和即可,时间复杂度n*n1/2

维护第二个数组,我们需要先枚举起始分块。

从起始分块开始,枚举终点分块,每枚举一个终点分块,更新这个分块内元素对于区间众数的贡献。

建立一个数组tmp[ i ]作为辅助数组表示颜色i出现次数。

for(int i=1;i<=get_pos(n);i++){//枚举起始分块
        int mx=0;//从当前这个起始分块开始,到终点分块位置的众数for(int j=i;j<=get_pos(n);j++)//枚举终点分块
            for(int k=(j-1)*len+1;k<=min(j*len,n);k++){
                tmp[a[k]]++;//当前元素出现次数加一
                if(tmp[a[k]]>tmp[mx])//若当前元素出现次数大于当前处理区间众数的出现次数,则将众数修改为当前元素
                    mx=a[k];
                if(!(tmp[a[k]]^tmp[mx]))//若当前元素与当前处理区间众数出现次数相等,则取编号小的为众数
                    mx=min(mx,a[k]);
            }
            b_mos[i][j]=mx;//从分块i到分块j的众数就是mx
        }
        for(int j=0;j<=ntot;j++)//清空辅助数组
            tmp[j]=0;                     
    }

时间复杂度为n1/2 *(n+n1/2*n1/2),即n3/2

处理询问

对于询问的l,r,算出其所处分块lb,rb。

若l与r在同一分块或在相邻块内,可以暴力求出众数,时间复杂度n1/2

int get_vio(){//vio指violent
    int mx=0;
    for(int i=l;i<=r;i++){
        tmp[a[i]]++;
        if(tmp[a[i]]>tmp[mx])//按比较规则进行更新。
            mx=a[i];
        if(!(tmp[a[i]]^tmp[mx]))
            mx=min(mx,a[i]);
    }
    for(int i=l;i<=r;i++)
        tmp[a[i]]--;
    return mx;
}

若l与r所在分块中间相隔了至少一个分块,那么所询问区间的众数只有两种可能。

  1. 中间那一段分块的众数
  2. 左端点所在块与右端点所在块内的数

不难理解。

对于中间那一段分块内的数,如果它们的出现次数要超过中间那一段分块内的众数,那么它们必须要在左端点所在块和右端点所在块内出现。

先将中间那一段分块的众数设为答案,再对左端点和右端点所在块内的数统计出现次数并更新答案即可。

int get_tim(int x){
    return tmp[x]+b_sum[rb-1][x]-b_sum[lb][x];//计算某数的总出现次数
}
int get_q(){
    int mx=b_mos[lb+1][rb-1];//先将答案设为中间那一段分块内的众数
    for(int i=l;i<=lb*len;i++){
        tmp[a[i]]++;
        if(get_tim(a[i])>get_tim(mx))//按照比较规则更新答案
            mx=a[i];
        if(get_tim(a[i])==get_tim(mx))
            mx=min(mx,a[i]);      
    }
    for(int i=(rb-1)*len+1;i<=r;i++){
        tmp[a[i]]++;
        if(get_tim(a[i])>get_tim(mx))
            mx=a[i];
        if(get_tim(a[i])==get_tim(mx))
            mx=min(mx,a[i]);   
    }     
    for(int i=l;i<=lb*len;i++)//清空辅助数组
        tmp[a[i]]--;
    for(int i=(rb-1)*len+1;i<=r;i++)
        tmp[a[i]]--;  
    return mx;
}

处理单词询问时间复杂度为n1/2

算法整体复杂度为(m+n)*n1/2,即n3/2,可以通过本题。

类似的题目还有  洛谷P4135

#include<bits/stdc++.h>
using namespace std;
const int h=40010;
const int b_h=210;
int len;
int get_pos(int x){//得到某个位置所在分块编号 
    return (x-1)/len+1;
}
int n,m;
int a[h],line[h];
int ntot=0,num[h];
map<int,int>rk;
int tmp[h];
int b_mos[b_h][b_h];
int b_sum[b_h][h];
void get_pre(){
    for(int i=1;i<=get_pos(n);i++)
        for(int j=1;j<=ntot;j++)
            b_sum[i][j]+=b_sum[i-1][j];           
    for(int i=1;i<=get_pos(n);i++){
        int mx=0;
        for(int j=i;j<=get_pos(n);j++){
            for(int k=(j-1)*len+1;k<=min(j*len,n);k++){
                tmp[a[k]]++;
                if(tmp[a[k]]>tmp[mx])
                    mx=a[k];
                if(!(tmp[a[k]]^tmp[mx]))
                    mx=min(mx,a[k]);
            }
            b_mos[i][j]=mx;
        }
        for(int j=0;j<=ntot;j++)
            tmp[j]=0;                     
    }
}
int l,r,lb,rb,lst=0,ans;
int get_vio(){
    int mx=0;
    for(int i=l;i<=r;i++){
        tmp[a[i]]++;
        if(tmp[a[i]]>tmp[mx])   
            mx=a[i];
        if(tmp[a[i]]==tmp[mx])
            mx=min(mx,a[i]);
    }
    for(int i=l;i<=r;i++)
        tmp[a[i]]--;
    return mx;
}
int get_tim(int x){//计算一个数的出现次数 
    return tmp[x]+b_sum[rb-1][x]-b_sum[lb][x];
}
int get_q(){
    int mx=b_mos[lb+1][rb-1];
    for(int i=l;i<=lb*len;i++){
        tmp[a[i]]++;
        if(get_tim(a[i])>get_tim(mx))
            mx=a[i];
        if(get_tim(a[i])==get_tim(mx)) 
            mx=min(mx,a[i]);      
    }
    for(int i=(rb-1)*len+1;i<=r;i++){
        tmp[a[i]]++;
        if(get_tim(a[i])>get_tim(mx))
            mx=a[i];
        if(get_tim(a[i])==get_tim(mx))
            mx=min(mx,a[i]);   
    }     
    for(int i=l;i<=lb*len;i++)
        tmp[a[i]]--;
    for(int i=(rb-1)*len+1;i<=r;i++)
        tmp[a[i]]--;  
    return mx;
}
int main(){
    scanf("%d%d",&n,&m);
    len=sqrt(n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),line[i]=a[i];
    sort(line+1,line+n+1);
    for(int i=1;i<=n;i++)//离散化 
        if(line[i]!=line[i-1])
            rk[line[i]]=++ntot,num[ntot]=line[i];
            
    for(int i=1;i<=n;i++) 
        a[i]=rk[a[i]];
        
    for(int i=1;i<=n;i++)
        b_sum[get_pos(i)][a[i]]++;
    get_pre();
    for(int i=1;i<=m;i++){
        scanf("%d%d",&l,&r);
        l=(l+lst-1)%n+1,r=(r+lst-1)%n+1;
        if(l>r)
            swap(l,r);
        lb=get_pos(l),rb=get_pos(r);
        if(lb>=rb-1)
            ans=get_vio();
        else
            ans=get_q();
        printf("%d\n",num[ans]),lst=num[ans];
    }
    return 0;
}
完整代码

标签:洛谷,分块,int,get,tim,众数,mx,P4168
From: https://www.cnblogs.com/12103h/p/16884242.html

相关文章

  • 洛谷P7223 [RC-04] 01 背包
    [RC-04]01背包题目描述P7223[RC-04]01背包-洛谷有一个容积为正无穷的背包,你要往里面放物品。你有\(n\)个物品,第\(i\)个体积为\(a_i\)。你有一个幸运数......
  • 洛谷 P7473 [NOI Online 2021 入门组] 重力球
    题面题目链接题解首先这题放在图论专题难度就下降了一大个档次ww我们注意到一点,就是如果你可以把两个点的位置记下来,一步操作之后,只会有四种可能的情况.两个点的位置......
  • 洛谷-3131
    洛谷-3131思路首先有一个\(brute-force\),从大到小枚举区间长度,然后通过前缀和找是否存在这样的区间,时间复杂度\(o(n^2)\)。在上述操作中,实际上我们做的就是找到两个下标......
  • 洛谷-4552
    洛谷-4552思路首先需要由区间加减法或者最后的形式(变为1个数)联想到差分在差分的形式下,只剩一个数意味着差分数组中\(2\dotsn\)都必须为0,其他任意。而给我们的操作......
  • 洛谷刷题_质数口袋
    P5723【深基4.例13】质数口袋题目链接:https://www.luogu.com.cn/problem/P5723知识点:埃氏筛原理:要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的倍数筛掉,......
  • 洛谷1923 排序
    洛谷1923错误这道题用的快排,但是非常卡时间,最后将快排优化,新学stl中nth_element(数组名,数组名+第k小元素,数组名+元素个数);将第k小元素找出,最后直接输出就行//判断k......
  • 洛谷B2079求出e的值
    解题思路:注意变量的类型就ok了,没什么不容易理解的地方#include<stdio.h>intmain(){doublenum=1,sum=1;longlongb=1,n;scanf("%d",&n);for(inti=1;i<=n......
  • 洛谷 P4423 [BJWC2011]最小三角形 题解
    求平面最近点对的时候有这样一种思路:将所有点全部绕原点旋转同一个角度,然后按横坐标排序。根据数学直觉,在随机旋转后,答案中的两个点在数组中肯定不会离得太远。把这种......
  • 洛谷刷题_ISBN 号码
    P1055[NOIP2008普及组]ISBN号码题目链接:https://www.luogu.com.cn/problem/P1055这道题从题意上来说还是比较简单的,刚开始想用整形直接输入一个一个数字,没有想到scan......
  • 洛谷刷题_三角形分类
    【深基3.习8】三角形分类题目描述给出三条线段a,b,c的长度,均是不大于10000的正整数。打算把这三条线段拼成一个三角形,它可以是什么三角形呢?如果三条线段不能组成一......