BZOJ4241 : 历史研究
莫队算法,考虑如何快速维护最大的重要度。
考虑到答案一共只有$O(n)$种本质不同的取值,于是可以先通过$O(n\log n)$的排序处理出这些值的大小关系,并将这些值离散化,同时对每种事件的每个出现次数维护两个指针pre和nxt,分别表示出现次数减少或增加一后是第几小。
然后对这些取值进行分块,每块维护该块内有哪些值出现过。
显然,修改是$O(1)$的。
查询的时候从后往前扫描,遇到第一个有数字的块就在该块内从后往前扫描,时间复杂度$O(\sqrt{n})$。
于是总的复杂度为$O((n+q)\sqrt{n})$。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; typedef long long ll; const int N=210000,K=9,BUF=6000000,OUT=2000000; struct query{int l,r,id;}ask[N]; int n,q,m,lim,i,pos[N],a[N],b[N],c[N],ap[N]; int pre[N],nxt[N],loc[N],cnt,sum[N],en[N];ll ans[N]; struct P{ll x;int y;P(){}P(ll _x,int _y){x=_x,y=_y;}}v[N]; inline bool cmpv(const P&a,const P&b){return a.x<b.x;} inline bool cmp(const query&a,const query&b){return pos[a.l]<pos[b.l]||pos[a.l]==pos[b.l]&&a.r<b.r;} inline int lower(int x){ int l=1,r=n,mid,t; while(l<=r)if(b[mid=(l+r)>>1]<=x)l=(t=mid)+1;else r=mid-1; return t; } inline void add(int x){ int&y=loc[x]; y=nxt[y],ap[y]=1,sum[y>>K]++; } inline void del(int x){ int&y=loc[x]; ap[y]=0,sum[y>>K]--,y=pre[y]; } inline ll askmax(){ for(int i=cnt;~i;i--)if(sum[i])for(int j=en[i];j;j--)if(ap[j])return v[j].x; } char Buf[BUF],*buf=Buf,Out[OUT],*ou=Out;int Outn[30],Outcnt; inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;} inline void write(ll x){ if(!x)*ou++=48; else{ for(Outcnt=0;x;x/=10)Outn[++Outcnt]=x%10+48; while(Outcnt)*ou++=Outn[Outcnt--]; } } int main(){ fread(Buf,1,BUF,stdin);read(n);read(q); lim=(int)sqrt(n+0.5); for(i=1;i<=n;i++)read(a[i]),b[i]=a[i],pos[i]=i/lim; sort(b+1,b+n+1); for(i=1;i<=n;i++)c[i]=lower(a[i]); for(i=1;i<=n;i++)v[++m]=P(0,i); for(i=1;i<=n;i++)ap[c[i]]++,v[++m]=P((ll)a[i]*ap[c[i]],c[i]); sort(v+1,v+m+1,cmpv); for(i=1;i<=n;i++)ap[i]=0; for(i=1;i<=m;i++){ if(ap[v[i].y])pre[i]=ap[v[i].y],nxt[ap[v[i].y]]=i;else loc[v[i].y]=i; ap[v[i].y]=en[i>>K]=i; } cnt=m>>K; for(i=1;i<=q;i++)read(ask[i].l),read(ask[i].r),ask[i].id=i; sort(ask+1,ask+q+1,cmp); for(i=1;i<=n;i++)ap[i]=1,sum[i>>K]++; int*l=c+1,*r=c; for(i=1;i<=q;i++){ int*L=c+ask[i].l,*R=c+ask[i].r; if(r<R){for(r++;r<=R;r++)add(*r);r--;} if(r>R)for(;r>R;r--)del(*r); if(l<L)for(;l<L;l++)del(*l); else if(l>L){for(l--;l>=L;l--)add(*l);l++;} ans[ask[i].id]=askmax(); } for(i=1;i<=q;i++)write(ans[i]),*ou++=10; fwrite(Out,1,ou-Out,stdout); return 0; }