传送门:bzoj2506
题解
非常妙的dp!
对于100以内,直接预处理。
100以上,直接找(1e4的范围使次数严格不大于100)
但这样还不够,
我们把询问离线下来,记录每个询问的左右端点,用2*n的结构体分别记录,然后sort一遍,直接从左到右扫,扫到一个点就记录一下,回答的时候拿终点和起点-1做前缀和的差。
orzzzzhzwer
代码
//made by cosi
#include<bits/stdc++.h>
#define gc getchar()
using namespace std;
const int N=1e5+10;
int ans[2][N],d[N],f[102][102],n,m,cnt,a[N];
struct P{
int x,p,k,f,num;
}t[N<<1];
bool cmp(const P& a,const P& b){return a.x<b.x;}
inline int rd()
{
char ch=gc;int x=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=gc;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=gc;}
return x*f;
}
inline void add(int x)
{
for(int i=1;i<=100;i++) f[i][a[x]%i]++;
d[a[x]]++;
}
int main(){
n=rd();m=rd();
for(int i=1;i<=n;i++) a[i]=rd();
for(int i=1;i<=m;i++){
int l=rd(),r=rd(),p=rd(),k=rd();
t[++cnt].x=l-1;t[cnt].p=p;t[cnt].k=k;t[cnt].num=i;
t[++cnt].x=r;t[cnt].p=p;t[cnt].k=k;t[cnt].num=i;t[cnt].f=1;
}
sort(t+1,t+cnt+1,cmp);
int now=0;
for(int i=1;i<=cnt;i++){
while(now<t[i].x){now++;add(now);}
int p=t[i].p,k=t[i].k;
if(p<=100) ans[t[i].f][t[i].num]=f[p][k];
else{
for(int j=k;j<=10000;j+=p) ans[t[i].f][t[i].num]+=d[j];
}
}
for(int i=1;i<=m;i++) printf("%d\n",ans[1][i]-ans[0][i]);
return 0;
}