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