BZOJ2506 : calc

在线做法:

若p不超过100,则可以先预处理,将所有满足a[x]%i=j的x从小到大放进链表q[i][j]中,查询时二分

预处理:$O(100n)$

查询:$O(\log n)$

若p超过100,则满足条件的a[x]一定不超过100种

于是维护一个二维数组T,T[i][j]表示在前j个数里多有少个的权值是i

为了高速查询,可以将序列按每块100的大小分块,维护一个块状数组

查询时枚举所有可能的a[x],然后利用前缀和O(1)询问

预处理:$O(100n)$

查询:$O(100)$

 

#include<cstdio>
const int N=100001,K=10001,S=101,B=1001;
int n,m,i,j,k,p,x,y,size=100,block,cnt,a[N],g[S],nxt[N],v[N],ed;
int st[S][S],en[S][S],q[N*S];
int pos[N],idx[N],pool[N][S],b[K][B],tag[K][B];
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
inline int vio(int p,int k,int l,int r){
  int t=0;
  for(;k<K;k+=p)t+=tag[k][pos[r]-1]-tag[k][pos[l]-1]+pool[b[k][pos[r]]][idx[r]]-pool[b[k][pos[l]]][idx[l]];
  return t;
}
inline int ask(int L,int R,int x){
  int l=L,r=R,t=R+1,mid;
  while(l<=r)if(q[mid=(l+r)>>1]<=x)r=(t=mid)-1;else l=mid+1;
  return R-t+1;
}
int main(){
  read(n),read(m);
  for(pos[0]=i=1;i<=n;i++){
    read(a[i]);
    if(!b[a[i]][pos[i]=(i-1)/size+1])b[a[i]][pos[i]]=++cnt;
    pool[b[a[i]][pos[i]]][idx[i]=(i-1)%size+1]=1,tag[a[i]][pos[i]]++;
  }
  for(block=pos[n],i=1;i<=cnt;i++)for(j=2;j<=size;j++)pool[i][j]+=pool[i][j-1];
  for(i=0;i<K;i++)for(j=2;j<=block;j++)tag[i][j]+=tag[i][j-1];
  for(cnt=0,j=1;j<=size;j++){
    for(ed=i=0;i<j;g[i++]=0);
    for(i=1;i<=n;i++)add(a[i]%j,i);
    for(i=0;i<j;en[j][i++]=cnt)for(st[j][i]=cnt+1,k=g[i];k;k=nxt[k])q[++cnt]=v[k];
  }
  while(m--){
    read(x),read(y),read(p),read(k),x--;
    printf("%d\n",p<=size?(ask(st[p][k],en[p][k],y)-ask(st[p][k],en[p][k],x)):vio(p,k,x,y));
  }
  return 0;
}

  

 

posted @ 2015-01-30 12:21  Claris  阅读(273)  评论(0编辑  收藏  举报