链接
http://codevs.cn/problem/5037/
题解
基本思想:分块记录每个块内各种数字的数量,然后统计,时空复杂度O(N*sqrt(N))。
像这种题目线段树一般做不了,因为有K种不同的数字(你不可能对每个节点都开一个大小为K的数组,但你完全可以对每一个块开一个),类似的还有K种颜色..etc。
代码
//分块
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 200100
#define maxsz 450
int lp[maxn+maxsz], cnt[maxn/maxsz+10][maxn], N, M, K, num[maxn], tag[maxn/maxsz+10],
size;
inline int read(int x=0)
{
char c=getchar();
while(c<48 or c>58)c=getchar();
while(c>=48 and c<=58)x=x*10+c-48,c=getchar();
return x;
}
inline char getc()
{
char c=getchar();
while(c<'a' or c>'z')c=getchar();
return c;
}
inline void init()
{
int i;
size=sqrt(N);
for(i=0;i<N;i++)
{
scanf("%d",num+i);
cnt[lp[i]=i/size][num[i]%=K]++;
}
for(i=N;i<=N+size;i++)lp[i]=i/size;
}
inline void add1(int x, int d)
{
cnt[lp[x]][num[x]]--;
cnt[lp[x]][num[x]=(num[x]+d)%K]++;
}
inline void add(int l, int r, int v)
{
int i;
for(i=l;lp[i]==lp[l] and i<=r;i++)add1(i,v);
if(lp[l]^lp[r])for(i=r;lp[i]==lp[r];i--)add1(i,v);
for(i=lp[l]+1;i<lp[r];i++)tag[i]+=v;
}
inline int count(int l, int r)
{
int i, ans=0, t;
for(i=l,t=tag[lp[l]]%K;lp[i]==lp[l] and i<=r;i++)
if((!num[i] and !t) or num[i]+t==K)ans++;
if(lp[l]^lp[r])for(i=r,t=tag[lp[r]]%K;lp[i]==lp[r];i--)
if((!num[i] and !t) or num[i]+t==K)ans++;
for(i=lp[l]+1;i<lp[r];i++)ans+=cnt[i][(K-(tag[i]%=K))^K?K-tag[i]:0];
return ans;
}
int main()
{
int l, r, v;
char type;
N=read(), M=read(), K=read();
init();
while(M--)
{
type=getc(), l=read()-1, r=read()-1;
if(type=='c')printf("%d\n",count(l,r));
else v=read()%K, add(l,r,v);
}
return 0;
}