BZOJ 2724 浅谈分块算法求区间众数

BZOJ 2724 浅谈分块算法求区间众数_第1张图片
世界真的很大
分块算法大概算是一种在线的数据结构,和线段树作用差不多,但是却能维护一些线段树维护不了的信息。
线段树要求维护的信息必须要具备可合并性,就是说子区间的信息能够合并到大区间里,比如区间和,区间值域等
但是分块却不需要这样的条件
有点暴力的味道但是确确实实是一种有效的数据结构
看题先:
description:
BZOJ 2724 浅谈分块算法求区间众数_第2张图片
input
BZOJ 2724 浅谈分块算法求区间众数_第3张图片

修正一下

l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1

output
这里写图片描述
题目也是有点迷
分块这种数据结构,和线段树等递归的结构不同,旨在将原序列分成若干“块”,操作的时候如果横跨整个块就给整个块打标记,单独在某个块的就暴力操作,以此来节约时间复杂度
如果横跨很多块,那么时间复杂度就是O(块的数目),对每个块的操作的时间复杂度就是O(块的大小),每次操作就是O(块的数目)+O(块的大小)的时间复杂度,而已知一共有n个节点,即O(块的大小)*O(块的数目)=n
所以由基本(均值)不等式得O(块的大小)+O(块的数目)>=2*(O(块的大小)*O(块的数目))^0.5
当且仅当O(块的大小)=O(块的数目)时,O(块的大小)+O(块的数目),即每次操作的时间复杂度最小,而O(块的大小)*O(块的数目)=n,所以块的数目和大小都应该是n^0.5
所以说每次操作的时间复杂度是n^0.5的,总时间复杂度应该是n^1.5
对于这道题来讲,对于一个大区间内的众数,在大区间包含的整块区间里的众数,大区间的众数要么是他,要么是两段不是整块的部分的数使得其数量超过了整块的众数
所以众数要么是整块区域的众数,要么是两端独立部分出现的数
查询时就比较独立部分,O(n^0.5),和整块众数谁出现的多就好,时间复杂度也是可以接受的
以此推得我们需要预处理的信息,
1.整块的区域的众数,即任意块i到任意块j的众数
2.任意数在任意区间出现了多少次
对于1,我们可以暴力枚举每个块,然后暴力算出其到之后所有块的众数,用个桶来处理就好,时间复杂度是(n^l.5)
对于2,我们可以对于每个数的值建一个vector,把这个数出现的位置压进去,每次用upperbound和lowerbound判断vector里L,到R范围内有多少个数就行了
还有一些细节
数据范围可能比较大,所以还是离散化一下比较好,记得记录一下离散之后的值和原值的对应关系
lowerbound和upperbound返回的值是其查询到的数据结构的位置信息,而对于vector,这样的动态数据结构,其返回的是一个迭代器的位置,由于vector元素之间,位置是连续的,所以可以直接相减判断之间有多少个元素
完整代码:

#include
#include
#include
#include 
#include
using namespace std;

struct A
{
    int sum,idx;
}aa[100010];

vector <int> src[100010];

int pos[100010],rnk[100010],wa[100010],f[1010][1010],b[100010];
int n,m,blk,ans=0,big=0,x=0;

bool cmp(const A &a, const A &b)
{
    return a.sumbool cmp2(const A &a, const A &b)
{
    return a.idxvoid init(int x)
{
    int ans=0,big=0;
    memset(wa,0,sizeof(wa));
    for(int i=(x-1)*blk+1;i<=n;i++)
    {
        int t=pos[i];
        wa[aa[i].sum]++;
        if(wa[aa[i].sum]>big) big=wa[aa[i].sum],ans=aa[i].sum;
        else if(wa[aa[i].sum]==big && aa[i].sumint Saber(int lf,int rg,int a)
{
    vector<int>::iterator x=upper_bound(src[a].begin(),src[a].end(),rg);
    vector<int>::iterator y=lower_bound(src[a].begin(),src[a].end(),lf);
    return x-y;
}

int query(int L,int R)
{
    int ans=f[pos[L]+1][pos[R]-1];
    int big=Saber(L,R,ans);
    for(int i=L;i<=min(R,pos[L]*blk);i++)
    {
        int tmp=Saber(L,R,aa[i].sum);
        if(tmp>big) big=tmp,ans=aa[i].sum;
        else if(tmp==big && aa[i].sumif(pos[L]!=pos[R])
        for(int i=(pos[R]-1)*blk+1;i<=R;i++)
        {
            int tmp=Saber(L,R,aa[i].sum);
            if(tmp>big) big=tmp,ans=aa[i].sum;
            else if(tmp==big && aa[i].sumreturn ans;
}

int main()
{
    scanf("%d%d",&n,&m);
    blk=(int) sqrt(n) ;
    for(int i=1;i<=n;i++)
        scanf("%d",&aa[i].sum),aa[i].idx=i;
    sort(aa+1,aa+n+1,cmp);
    int tot=0;
    for(int i=1;i<=n;i++)
    {
        if(aa[i].sum!=aa[i-1].sum)
            tot++;
        b[i]=tot;
        rnk[tot]=aa[i].sum;
    }
    for(int i=1;i<=n;i++) aa[i].sum=b[i];
    sort(aa+1,aa+n+1,cmp2);
    for(int i=1;i<=n;i++) src[aa[i].sum].push_back(i);
    for(int i=1;i<=n;i++) pos[i]=(i-1)/blk+1;
    for(int i=1;i<=pos[n];i++) init(i);
    for(int i=1;i<=m;i++)
    {
        int L,R;
        scanf("%d%d",&L,&R);
        L=(L+x-1)%n+1,R=(R+x-1)%n+1;
        if(L>R) swap(L,R);
        x=rnk[query(L,R)];
        printf("%d\n",x);
    }
    return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/

嗯,就是这样

你可能感兴趣的:(数据结构)