转自:http://www.cnblogs.com/wuyiqi/archive/2012/05/27/2520642.html
有n个数,刚开始都为0
add i , j 给i,j区间内的数都加1
Q i j 询问i、j间能被三整除的数的个数
线段树记录三个域
对三取余为0的数的个数
。。。。。1.。。。。。
。。。。。2.。。。。。
可以保存在一个数组里面
考虑到每次给一个区间加1的时候,区间内对3取余为1的数的个数变成了对三取余为2,2的变成了0,0的变成了1
所以每次更新到区间或者把信息(懒惰标记)往下传的时候只需要把相应的域做一下调整即可
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 100010;
int cov[maxn<<2];
int sum[maxn<<2][3];
void pushup(int rt){
for(int i=0;i<3;i++){
sum[rt][i]=sum[rt<<1][i]+sum[rt<<1|1][i];
}
}
void build(int l,int r,int rt){
cov[rt]=0;
if(l==r){
sum[rt][0]=1;
sum[rt][1]=sum[rt][2]=0;
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void make(int rt){
int tmp=sum[rt][0];
sum[rt][0]=sum[rt][2];
sum[rt][2]=sum[rt][1];
sum[rt][1]=tmp;
}
void pushdown(int rt){
if(cov[rt]){
cov[rt<<1]+=cov[rt];
cov[rt<<1|1]+=cov[rt];
for(int i=0;i<cov[rt]%3;i++){
make(rt<<1);
make(rt<<1|1);
}
cov[rt]=0;
}
}
void update(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
cov[rt]++;
make(rt);
return ;
}
pushdown(rt);
int m=(l+r)>>1;
if(L<=m) update(L,R,lson);
if(R>m) update(L,R,rson);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return sum[rt][0];
}
pushdown(rt);
int m=(l+r)>>1;
int ret=0;
if(L<=m) ret+=query(L,R,lson);
if(R>m) ret+=query(L,R,rson);
return ret;
}
int main(){
int t,ca=1,i,j,k,a,b,n,q,op;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&q);
printf("Case %d:\n",ca++);
build(1,n,1);
while(q--){
scanf("%d%d%d",&op,&a,&b);a++;b++;
if(op==0) update(a,b,1,n,1);
else printf("%d\n",query(a,b,1,n,1));
}
}
return 0;
}