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

点此看题面

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

莫队

这道题的算法应该是莫队

如何用莫队求解此题

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

当我们要加入一个新数(x)的时候,如果加入前的(cnt_x=ans),则加入后的(cnt_x)肯定大于(ans),因此将(ans)(1)

当我们要删除一个数(x)的时候,如果删除前(cnt_x=ans)(tot_{ans}=1),则删除该元素后就没有元素的出现个数为(ans)了,因此将(ans)(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('
');//按读入的顺序输出答案
    return fwrite(Fout,1,FoutSize,stdout),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3709.html