[日常摸鱼]bzoj2724蒲公英-分块

区间众数经典题~

http://begin.lydsy.com/JudgeOnline/problem.php?id=4839
这里可以提交~

题意大概就是没有修改的询问区间众数,如果有一样的输出最小的,强制在线,$n \leq 4*10^4,a_i \leq 10^9$。

 


 

 

log数据结构脑补一遍好像没什么可以做的,数据范围我们可以分块!

不过分块之前肯定要离散化一下,而且还要保存离散化前的数据(因为要回答的是出现最多的数),离散化的方法在上一篇博客里面~

假设分成$L$块,每块大小$s=\lfloor n/L \rfloor$,预处理出每一块的起点和终点$st[],ed[]$,然后再预处理出$O(L^2)$个连续的块里的答案(每种颜色的出现次数,最大次数和对应的颜色),这样预处理的复杂度是$O(n*L^2)$。

然后对于一个区间的询问$[x,y]$,一定能拆成连续的若干个块(假设对应的块是$[p,q]$)和至多两个不完整的块,对于中间连续的块直接用我们之前预处理的答案。两边的暴力更新。

一种更新方法是把两边的颜色都加到中间完整的一段去,然后更新答案,更新完之后再减回去。复杂度$O(\frac{mn}{L})$,于是总复杂度$O(n*L^2+\frac{mn}{L})$,$m,n$同阶,均值不等式告诉我们当$L=n^{\frac{1}{3}}$的时候复杂度取最小值为$O(n^{\frac{5}{3}})$

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define rep(i,n) for(register int i=1;i<=n;i++)
#define REP(i,a,b) for(register int i=a;i<=b;i++)
#define debug(x) printf("%s =  %d , ",#x,x)
using namespace std;
const int T=40;
const int N=40005;
inline int read()
{
    int s=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
    while(c>='0'&&c<='9'){s=s*10+c-'0';c=getchar();}
    return f?s:-s;
}
int n,Q,L,s,ans,cnt,tot,num,p,q;
int w[N],tw[N],v[N],b[N],c[T][T][N],f[T][T],d[T][T],st[T],ed[T];
inline void prework()
{
    n=read();Q=read();
    rep(i,n)w[i]=read();
    
    L=(int)pow(n,1.0/3.0);
    if(L)s=n/L;
    rep(i,L)st[i]=(i-1)*s+1,ed[i]=i*s;
    if(ed[L]!=n)st[L+1]=ed[L]+1,ed[++L]=n;
    
    memcpy(tw,w,sizeof tw);sort(tw+1,tw+n+1);
    rep(i,n)if(i==1||tw[i]!=tw[i-1])v[++tot]=tw[i];
    rep(i,n)b[i]=lower_bound(v+1,v+tot+1,w[i])-v;
    REP(i,1,L)REP(j,i,L)
    {
        rep(k,tot)c[i][j][k]=c[i][j-1][k];
        REP(k,st[j],ed[j])c[i][j][b[k]]++;
        rep(k,tot)if(c[i][j][k]>f[i][j]||(c[i][j][k]==f[i][j]&&tw[k]<d[i][j]))
            f[i][j]=c[i][j][k],d[i][j]=k;
    }
}
inline void updata(int i)
{
    c[p][q][b[i]]++;
    if(c[p][q][b[i]]>cnt||(c[p][q][b[i]]==cnt&&b[i]<num))cnt=c[p][q][b[i]],num=b[i];
}
inline int solve(int x,int y)
{
    if(x>y)swap(x,y);
    int l,r;
    for(register int i=1;i<=L;i++)if(x<=ed[i]){l=i;break;}
    for(register int i=L;i>=1;i--)if(y>=st[i]){r=i;break;}
    if(l+1<=r-1)p=l+1,q=r-1;
    else p=q=0;
    cnt=f[p][q];num=d[p][q];
    if(l==r)
    {
        REP(i,x,y)updata(i);
        REP(i,x,y)c[p][q][b[i]]--;
    }else
    {
        REP(i,x,ed[p-1])updata(i);
        REP(i,st[q+1],y)updata(i);
        REP(i,x,ed[p-1])c[p][q][b[i]]--;
        REP(i,st[q+1],y)c[p][q][b[i]]--;
    }
    return v[num];
}

int main()
{
    prework();
    rep(i,Q)
    {
        int l,r;
        l=read();r=read(); 
        ans=solve((l+ans-1)%n+1,(r+ans-1)%n+1);
        printf("%d\n",ans);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/yoooshinow/p/8413132.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值