• 【洛谷5048】[Ynoi2019 模拟赛] Yuno loves sqrt technology III(分块妙题)


    点此看题面

    • 给定一个长度为(n)的序列,(q)次询问区间众数的出现次数。
    • (n,qle5 imes10^5),强制在线

    分块预处理+询问

    不得不说是非常妙的一道题。

    考虑预处理的时候我们求出(f_{i,j})表示(isim j)的块中的区间众数的出现次数,这只要枚举起始块(i)然后不断往里面加数即可,时间复杂度(O(nsqrt n)),空间复杂度(O(n))

    对于询问,我们先初始化答案为中间那些完整块的众数出现次数。

    然后考虑两边的散块,我们初始对于每种值开个(vector)存在它在序列中出现的位置,并对于每个位置记录它在其值对应的(vector)中的下标。

    那么我们枚举左边的散块中的数,就只要判断从它向后算起第(ans+1)个数是否还在询问区间内,能就不断将(ans)(1)

    同理,枚举右边散块中的数判断从它向前算起第(ans+1)个数是否还在询问区间内更新答案。

    显然散块中数的个数是(O(sqrt n))的,那么答案最多变化(O(sqrt n)),复杂度正确。

    代码:(O(nsqrt n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 500000
    #define M 720
    using namespace std;
    int n,ans,a[N+5],sz,bl[N+5],dc,dv[N+5],rk[N+5];vector<int> p[N+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    }using namespace FastIO;
    int g[N+5],f[M+5][M+5];I void Build()//初始化
    {
    	RI i,j,k;for(i=1;i<=bl[n];++i)//枚举起始块
    	{
    		for(j=i;j<=bl[n];++j) for(f[i][j]=f[i][j-1],//不断往里面加块
    			k=(j-1)*sz+1;k<=min(j*sz,n);++k) ++g[a[k]]>f[i][j]&&(f[i][j]=g[a[k]]);//更新最大出现次数
    		for(j=(i-1)*sz+1;j<=n;++j) --g[a[j]];//清空桶
    	}
    }
    I void BF_L(CI l,CI r,CI R)//暴力做左边的散块
    {
    	for(RI i=l;i<=r;++i) W(rk[i]+ans<p[a[i]].size()&&p[a[i]][rk[i]+ans]<=R) ++ans;//从它向后第ans+1个数
    }
    I void BF_R(CI l,CI r,CI L)//暴力做右边的散块
    {
    	for(RI i=l;i<=r;++i) W(rk[i]>=ans&&p[a[i]][rk[i]-ans]>=L) ++ans;//从它向前第ans+1个数
    }
    int main()
    {
    	RI Qt,i;for(read(n,Qt),sz=sqrt(n),i=1;i<=n;++i) read(a[i]),dv[i]=a[i],bl[i]=(i-1)/sz+1;
    	for(sort(dv+1,dv+n+1),dc=unique(dv+1,dv+n+1)-dv-1,i=1;i<=n;++i)//离散化
    		rk[i]=p[a[i]=lower_bound(dv+1,dv+dc+1,a[i])-dv].size(),p[a[i]].push_back(i);//记录每个位置在对应vector中的下标
    	RI l,r;Build();W(Qt--) read(l,r),l^=ans,r^=ans,bl[l]==bl[r]?(ans=0,BF_L(l,r,r))//同个块中直接暴力
    		:(ans=f[bl[l]+1][bl[r]-1],BF_L(l,bl[l]*sz,r),BF_R((bl[r]-1)*sz+1,r,l)),writeln(ans);//初始化答案为整块答案,暴力做散块
    	return clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    Ubuntu之修改用户名和主机名
    HM中CU,TU的划分
    BZOJ 3237([Ahoi2013]连通图-cdq图重构-连通性缩点)
    Introducing Regular Expressions 学习笔记
    kubuntu添加windows字体
    WISE安装程序增加注册控制
    Linux内核中常见内存分配函数(一)
    Linux内核中常见内存分配函数(二)
    Swift现实
    Android 5.0(L) ToolBar(替代ActionBar) 现实(四)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5048.html
Copyright © 2020-2023  润新知