题目链接:http://www.spoj.pl/problems/MULTQ3
http://vjudge.net/contest/view.action?cid=52045#problem/C
1.题意:
维护一段数列,操作一区间加一(update),操作二区间查询(query)模3为0的数的个数;
2.题解:
(1)首先要有线段树的基础;
(2)用三个标记v0,v1,v2分别记录:余0有几个数,余1有几个数,余2有几个数;
(3)用一个lazy标记记录当前区间被加了几次,lazy可取为0,1,2;
(4)每次update从根节点开始,第一步pushdown(把自己的lazy标记累加到儿子节点,并且根据自己的lazy标记的值更新儿子节点的v0,v1,v2,然后把自己的已经下传完毕的lazy标记取消),第二步递归调用update(把每个儿子节点的值也处理一遍使其成为正确的值,对于下传到刚好满足(ql<=l && r<=qr)的节点,直接把这个节点的v0,v1,v2的值修改正确,lazy标记也要加一,但是不下传了,原因是每次update我都保证每个被update访问过的节点是正确的值,所以现在这个刚好满足(ql<=l && r<=qr)的节点先前已经是正确的值了,所以我只要计算这一次操作过后的状态就行了,也就是直接把这个节点的v0,v1,v2的值修改正确,lazy标记也加一,但是不下传),第三步pushup(用(update递归更新)得到正确值的儿子节点去更新父亲节点,由于父亲节点的lazy标记已经给下传了,所以只有最下面的刚好满足(ql<=l && r<=qr)的节点此时才会有lazy标记);
(5)查询query(由于你不知道这个节点是之前刚好满足(ql<=l && r<=qr)的节点(打过lazy标记,但这个lazy标记也可能被后来的update操作中的pushdown或者query中的pushdown又传递给儿子了),还是已经被update操作中pushup更新成正确值的节点,你需要在query递归查询子区间之前,调用pushdown把当前节点的lazy标记传给子节点,并且把这次lazy标记对子节点单独造成的v0,v1,v2的变化描述出来,使得子节点是正确的值);
(6)初始化Build时一边构造一边pushup,使得当前节点的值是确定。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN=111111,MOD=3;
int v[4*MAXN][3],lazy[4*MAXN];
int ql,qr;
int ans;
void pushup(int o){
for(int i=0;i<3;i++)
v[o][i]=v[o<<1][i]+v[(o<<1)|1][i];
}
void pushdown(int o){
int ls=o<<1,rs=ls|1,tmp;
if(lazy[o]){
lazy[ls]=(lazy[ls]+lazy[o])%MOD;
lazy[rs]=(lazy[rs]+lazy[o])%MOD;
if(lazy[o]==1){
tmp=v[ls][0];
v[ls][0]=v[ls][2];
v[ls][2]=v[ls][1];
v[ls][1]=tmp;
}else if(lazy[o]==2){
tmp=v[ls][0];
v[ls][0]=v[ls][1];
v[ls][1]=v[ls][2];
v[ls][2]=tmp;
}
if(lazy[o]==1){
tmp=v[rs][0];
v[rs][0]=v[rs][2];
v[rs][2]=v[rs][1];
v[rs][1]=tmp;
}else if(lazy[o]==2){
tmp=v[rs][0];
v[rs][0]=v[rs][1];
v[rs][1]=v[rs][2];
v[rs][2]=tmp;
}
lazy[o]=0;
}
}
void Build(int o,int l,int r){
lazy[0]=0;
if(l==r){
v[o][0]=1;
v[o][1]=v[o][2]=0;
return;
}
int m=(l+r)>>1;
Build(o<<1,l,m);
Build((o<<1)|1,m+1,r);
pushup(o);
}
void update(int o,int l,int r){
if(ql<=l && r<=qr){
lazy[o]=(lazy[o]+1)%MOD;
int tmp=v[o][0];
v[o][0]=v[o][2];
v[o][2]=v[o][1];
v[o][1]=tmp;
return;
}
pushdown(o);
int M=(l+r)/2,ls=o<<1,rs=ls|1;
if(ql<=M) update(ls,l,M);
if(qr>M) update(rs,M+1,r);
pushup(o);
}
void Query(int o,int l,int r){
if(ql<=l && r<=qr){
ans+=v[o][0];
return;
}
pushdown(o);
int M=(l+r)>>1;
if(ql<=M) Query(o<<1,l,M);
if(qr>M) Query((o<<1)|1,M+1,r);
}
int main()
{
// freopen("data.in","r",stdin);
int N,Q,op,a,b;
while(scanf("%d%d",&N,&Q)!=EOF){
Build(1,1,N);
while(Q--){
scanf("%d%d%d",&op,&a,&b);
ql=a+1,qr=b+1;
if(op==1){
ans=0;
Query(1,1,N);
printf("%d\n",ans);
}else
update(1,1,N);
}
}
return 0;
}