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;
}

  

posted @ 2015-08-18 00:31  Claris  阅读(945)  评论(0编辑  收藏  举报