【洛谷3709】大爷的字符串题(离散化+莫队)

本文介绍了一种使用莫队算法解决区间内众数出现次数问题的方法。通过离散化处理和计数数组,算法能在多组询问中高效地找到指定区间内众数的出现次数,适用于数据规模较大的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点此看题面

大致题意: 算法标签——语文,给定一个数列,多组询问,每次询问一个区间内的众数出现的次数。


莫队

这道题的算法应该是莫队

L i n k Link Link

莫队算法详见博客莫队算法学习笔记(一)——普通莫队


如何用莫队求解此题

我们可以用 c n t cnt cnt数组记录下每个数出现的次数(注意要先离散化),用 t o t tot tot数组记录下每个数在 c n t cnt cnt数组中的出现次数,并用 a n s ans ans记录答案。

当我们要加入一个新数 x x x的时候,如果加入前的 c n t x = a n s cnt_x=ans cntx=ans,则加入后的 c n t x cnt_x cntx肯定大于 a n s ans ans,因此将 a n s ans ans 1 1 1

当我们要删除一个数 x x x的时候,如果删除前 c n t x = a n s cnt_x=ans cntx=ans t o t a n s = 1 tot_{ans}=1 totans=1,则删除该元素后就没有元素的出现个数为 a n s ans ans了,因此将 a n s ans ans 1 1 1

这样就可以了。


代码
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<0?-(x):(x))
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
#define N 200000
int FoutSize=0,OutputTop=0;char Fin[Fsize],*FinNow=Fin,*FinEnd=Fin,Fout[Fsize],OutputStack[Fsize];
using namespace std;
int n,Q,blo,s,a[N+5],p[N+5],bl[N+5],cnt[N+5],tot[N+5],res[N+5];
struct Query
{
    int l,r,pos;
}q[N+5];
inline void read(int &x)
{
    x=0;static char ch;
    while(!isdigit(ch=tc()));
    while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
}
inline void write(int x)
{
    if(!x) return (void)pc('0');
    while(x) OutputStack[++OutputTop]=x%10+48,x/=10;
    while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;
}
inline bool cmp(Query x,Query y)//将询问排序
{
    return bl[x.l]^bl[y.l]?bl[x.l]<bl[y.l]:(bl[x.l]&1?x.r<y.r:x.r>y.r);
}
inline int find(int x)//求出离散化后的值
{
    register int l=1,r=s,mid;
    while(l<=r) p[mid=l+r>>1]<x?l=mid+1:r=mid-1;//二分查找
    return l;
}
int main()
{
    register int i;
    for(read(n),read(Q),blo=sqrt(n),i=1;i<=n;++i) read(a[i]),p[i]=a[i],bl[i]=(i-1)/blo+1;
    for(sort(p+1,p+n+1),s=unique(p+1,p+n+1)-p-1,i=1;i<=n;++i) a[i]=find(a[i]);//将原数组离散化
    for(i=1;i<=Q;++i) read(q[q[i].pos=i].l),read(q[i].r);
    register int L=1,R=1,ans=tot[1]=1;
    for(sort(q+1,q+Q+1,cmp),cnt[a[1]]=i=1;i<=Q;++i)//以下为莫队的主要过程
    {
        while(R<q[i].r) {++R,--tot[cnt[a[R]]],++tot[++cnt[a[R]]];if(cnt[a[R]]>ans) ++ans;}//将区间右边界增加
        while(L>q[i].l) {--L,--tot[cnt[a[L]]],++tot[++cnt[a[L]]];if(cnt[a[L]]>ans) ++ans;}//将区间的左边界减小
        while(R>q[i].r) {if(!--tot[cnt[a[R]]]&&!(cnt[a[R]]^ans)) --ans;++tot[--cnt[a[R--]]];}//将区间的右边界减小
        while(L<q[i].l) {if(!--tot[cnt[a[L]]]&&!(cnt[a[L]]^ans)) --ans;++tot[--cnt[a[L++]]];}//将区间的左边界增大
        res[q[i].pos]=ans;//用res[]数组存储答案
    }
    for(i=1;i<=Q;++i) (res?pc('-'):0),write(res[i]),pc('\n');//按读入的顺序输出答案
    return fwrite(Fout,1,FoutSize,stdout),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值