SPOJ MULTQ3 线段树
来源:互联网 发布:java drawimage 厘米 编辑:程序博客网 时间:2024/05/20 04:29
题目链接: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;}
- SPOJ MULTQ3 线段树
- SPOJ GSS3 线段树
- SPOJ GGS1 线段树
- SPOJ DWARFLOG 线段树
- SPOJ IITWPC4F 线段树
- SPOJ BGSHOOT 线段树
- SPOJ GSS1 [线段树]
- SPOJ 375 (树链剖分+线段树)
- SPOJ 375 (树链剖分+线段树)
- spoj 61 Brackets(线段树)
- spoj 61 Brackets(线段树)
- SPOJ GSS2 离线线段树
- SPOJ GSS1 第一次线段树
- spoj LITE(线段树)
- SPOJ 1557 GSS2 线段树
- Spoj Query on a tree SPOJ - QTREE(树链剖分+线段树)
- SPOJ 3267 主席树||线段树+离线
- Spoj 375 Qtree 树链剖分 + 线段树 解法
- 树莓派学习笔记——定时向yeelink上传树莓派CPU温度
- 普通视图和物化视图的区别
- lua学习初探【二】
- 面对不断升级的内核,如何学习linux设备驱动
- GoConvey_初步认识
- SPOJ MULTQ3 线段树
- 基数排序
- Sweet and Sour Rock+spoj+简单dp
- ThreadLocal的几种误区
- Python:== 和 is
- JavaFX文档(6)开始JavaFX之旅——4 使用FXML来创建用户界面
- 欧拉回路,欧拉道路
- C++之练习题29
- Netfilter之DNAT和SNAT