题目分析
首先,这题虽然叫做线段树练习4,但是好像不能用线段树做?可能也是我太弱了,树王大佬应该就能用线段树做吧。
那么这题最好用的数据结构是分块!
好吧我不说了,解析都在代码里。
代码
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
#define ll long long
int read(){
int q=0;char ch=' ';
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();
return q;
}
const int maxn=200505;
int mod,n,m,sqn;
int pos[maxn],a[maxn],laz[maxn],sum[500][maxn];
//pos:记录每个位置在哪一块,a:每个位置的数%mod后的数,laz:懒惰标记,表示
//这个块i里的数都要加上laz[i],sum表示每个块里%mod余1,2,3...的数的个数
void add(int x,int y,int z){
int i,j,lim;
lim=min(pos[x]*sqn,y);
for(i=x;i<=lim;i++){
sum[pos[i]][a[i]]--;
a[i]=(a[i]+z)%mod;
sum[pos[i]][a[i]]++;
}
if(pos[x]!=pos[y]){
lim=(pos[y]-1)*sqn+1;
for(i=lim;i<=y;i++){
sum[pos[i]][a[i]]--;
a[i]=(a[i]+z)%mod;
sum[pos[i]][a[i]]++;
}
}
for(i=pos[x]+1;i<pos[y];i++){laz[i]+=z;laz[i]%=mod;}
}
int find(int x,int y){
int i,j,re=0,lim;
lim=min(pos[x]*sqn,y);
for(i=x;i<=lim;i++)if((a[i]+laz[pos[i]])%mod==0)re++;
if(pos[x]!=pos[y]){
lim=(pos[y]-1)*sqn+1;
for(i=lim;i<=y;i++)
if((a[i]+laz[pos[i]])%mod==0)re++;
}
for(i=pos[x]+1;i<pos[y];i++)re+=sum[i][(mod-laz[i])%mod];
//注意这里,如果laz[i]=0的话,应该查的是sum[i][0]而非sum[i][mod]
return re;
}
int main()
{
int i,x,y,z;char ch[10];
n=read();m=read();mod=read();sqn=sqrt(n);
for(i=1;i<=n;i++){
pos[i]=(i-1)/sqn+1;
a[i]=read();a[i]%=mod;sum[pos[i]][a[i]]++;
}
for(i=1;i<=m;i++){
scanf("%s",ch);
if(ch[0]=='a'){x=read();y=read();z=read();add(x,y,z);}
else {x=read();y=read();printf("%d\n",find(x,y));}
}
return 0;
}