蒲公英(bzoj2724)(分块+区间众数)

T2des(6).gif

Input

T2input(6).gif

Output

T2output(5).gif

Sample Input

6 3 
1 2 3 2 1 2 
1 5 
3 6 
1 5

Sample Output

1 
2 
1 

HINT

\(n <= 40000\),$ m <= 50000$

题意:

求区间众数

题解:

见代码

//解决本题的重要性质: 
//对于两个区间a,b,其中已知a区间的众数k 
//则众数一定为k或是b区间的任意一个数  
#include<bits/stdc++.h>
#define re register int 
using namespace std;
const int N=40010,M=410;
int n,q,m,blen,bsum;
int a[N],b[N];//b为离散数组 
int bl[M][M];//bl[i][j]表示第i个块中的第j个数,bl[i][0]表示第i个块的长度 
int bk[N];//bk[i]表示第i个数(在原数列中)在第bk[i]个块中 
int f[M][M];//f[i][j]表示第i块到第j块之间的众数 
int g[N][M];//g[i][j]表示i在前j个块中出现的次数 
void init(){//初始化 
    for(int i=1,j=1;i<=n;++j){
        int k;
        for(k=1;k<=blen&&i<=n;++i,++k){
            bk[i]=j;
            bl[j][k]=a[i];
        }k--;
        bl[j][0]=k;
        bsum=j;
    }//处理块 
    for(int i=1;i<=bsum;++i){
        for(int j=1;j<=m;++j){
            g[j][i]=g[j][i-1];
        }
        for(int j=1;j<=bl[i][0];++j){
            g[bl[i][j]][i]++;
        }
    }//预处理g数组 
    for(int i=1;i<=bsum;++i){
        for(int j=i;j<=bsum;++j){
            int num=f[i][j-1];int mx=g[num][j]-g[num][i-1];
            for(int k=1;k<=bl[j][0];++k){
                int now=g[bl[j][k]][j]-g[bl[j][k]][i-1];
                if(now>mx||(now==mx&&bl[j][k]<num))num=bl[j][k],mx=now;
            }
            f[i][j]=f[j][i]=num;
        }
    }//预处理f数组 
}
void read(){//读入 
    cin>>n>>q;blen=sqrt(n);
    for(int i=1;i<=n;++i)scanf("%d",a+i),b[i]=a[i];
}
void lsh(){
    sort(b+1,b+n+1);
    m=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;++i)a[i]=lower_bound(b+1,b+m+1,a[i])-b;
}
void work(){
    int last=0;
    while(q--){
        int l,r;
        scanf("%d%d",&l,&r);
        l=(l+last-1)%n+1;
        r=(r+last-1)%n+1;
        if(l>r)swap(l,r);
        static int bj[M],cnt,v[N];cnt=0;//bj记录边角的数据,cnt为边角数据的数量
        int L,R,num,mx; 
        if(bk[l]==bk[r]){//在同一块内暴力求众数 
            for(int i=l;i<=r;++i)bj[++cnt]=a[i],v[a[i]]++;
            mx=0;
            for(int i=l;i<=r;++i){
                if(v[a[i]]>mx||(v[a[i]]==mx&&a[i]<num))num=a[i],mx=v[a[i]];
            }
            printf("%d\n",last=b[num]);
        }else{//在不同块时,将中间当成一大块和边角比较
              //根据性质,众数只有可能是中间这一块的众数或是边角上的数
              //所以暴力枚举再判断就行了 
            re i;
            for(i=l;bk[i]==bk[i-1];++i){
                bj[++cnt]=a[i];v[a[i]]++;
            }L=bk[i]; 
            for(i=r;bk[i]==bk[i+1];--i){
                bj[++cnt]=a[i];v[a[i]]++;
            }R=bk[i];
            num=f[L][R],mx=v[num]+g[num][R]-g[num][L-1];
            for(i=1;i<=cnt;++i){
                int now=v[bj[i]]+g[bj[i]][R]-g[bj[i]][L-1];
                if(now>mx||(now==mx&&bj[i]<num))num=bj[i],mx=now;
            }
            printf("%d\n",last=b[num]);
        }
        for(re i=1;i<=cnt;++i)--v[bj[i]];//v数组要这样清空,复杂度O(cnt),不能用memset,那样是O(n)
    }
}
int main(){
    read();
    lsh();
    init();
    work(); 
}

转载于:https://www.cnblogs.com/zhenglier/p/10100608.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值