Description
看着虫虫重写的铁路购票系统使用非常方便,xyiyy想要虫虫帮忙实现一个鸡排销售情况查询的系统,主要是针对XX路上销售情况的查询。已知在XX路上,从东往西共有n个住户,标号分别为1,2……n-1,n,初始时,所有住户购买的鸡排数都为0。现只要求实现两个非常简单的功能,就是更新销售信息和查询[L,R]区间内有几位住户购买的鸡排数为3的倍数。系统命令的表示如下:
1)0 L R,表示标号在[L,R]范围内的所有住户都购买了一块鸡排。
2)1 L R,询问标号在[L,R]范围内购买的鸡排数目为3的倍数的住户数。
现有已知有Q条该系统的操作记录,但虫虫很忙,xyiyy希望你能帮忙实现这个系统。
Input
第一行包含两个数字n,Q,表示住户的数目,1<=n,Q<=100000。
接下来Q行由三个数字组成,0,L,R或者1,L,R,分别表示两种操作,1<=L,R<=n。
Output
对于每一次的询问操作输出购买的鸡排数目为3的倍数的住户数
Sample Input
4 7
1 1 4
0 2 3
0 2 4
1 1 1
0 1 4
1 4 4
1 1 4
1 1 4
0 2 3
0 2 4
1 1 1
0 1 4
1 4 4
1 1 4
Sample Output
4
1
0
2
题目大意:求给定区间内的数为为3的倍数的数的个数。
大致思路:很直白的线段树。最开始老是想着单点更新才能统计某一个数到底是不是三的倍数,当时听做出来的人一直说“模三循环”感觉有点意思然完全不知道应该怎么写。后来又仔细去扣这句话扣了大半天发现也不过就三种情况n%3==0 , n%3==1 , n%3==2 无非这三种可能,也不太懂怎么个循环法....后来尝试分别用zero[rt],one[rt],two[rt]来表示区间内数字为3的倍数余0,余1,余2的个数。那么每次询问的就是zero[rt]. 。
再来解决单点更新的问题。感觉不是很好想,想了很久仔细把样例手动模拟一遍才发现如果在整个区间上加上一的话,原本区间内余数为二的就会加一变成余数为0的,另外两个会同理跟着一起改变,实现了循环: 1->2,2->3,3->1. 而加上二的话就每个循环移两位:1->3,2->1,3->2,加上三就不影响。这样就每次可以在区间更新时用上lazy tag不必每次更新到叶子结点了。
1
0
2
题目大意:求给定区间内的数为为3的倍数的数的个数。
大致思路:很直白的线段树。最开始老是想着单点更新才能统计某一个数到底是不是三的倍数,当时听做出来的人一直说“模三循环”感觉有点意思然完全不知道应该怎么写。后来又仔细去扣这句话扣了大半天发现也不过就三种情况n%3==0 , n%3==1 , n%3==2 无非这三种可能,也不太懂怎么个循环法....后来尝试分别用zero[rt],one[rt],two[rt]来表示区间内数字为3的倍数余0,余1,余2的个数。那么每次询问的就是zero[rt]. 。
再来解决单点更新的问题。感觉不是很好想,想了很久仔细把样例手动模拟一遍才发现如果在整个区间上加上一的话,原本区间内余数为二的就会加一变成余数为0的,另外两个会同理跟着一起改变,实现了循环: 1->2,2->3,3->1. 而加上二的话就每个循环移两位:1->3,2->1,3->2,加上三就不影响。这样就每次可以在区间更新时用上lazy tag不必每次更新到叶子结点了。
#include<iostream>
using namespace std;
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int N=1e5+5;
LL add[N<<2];
LL zero[N<<2],one[N<<2],two[N<<2];
void PushUP(int rt)
{
zero[rt]=zero[rt<<1]+zero[rt<<1|1]; //每个区间的余0,1,2的值为左右孩子的和
one[rt]=one[rt<<1]+one[rt<<1|1];
two[rt]=two[rt<<1]+two[rt<<1|1];
}
void solve(int rt){ //循环移动一位的情况
int temp=zero[rt];
zero[rt]=two[rt];
two[rt]=one[rt];
one[rt]=temp;
}
void solve2(int rt){ //循环移动两位的情况
int temp=zero[rt];
zero[rt]=one[rt];
one[rt]=two[rt];
two[rt]=temp;
}
void PushDown(int rt,int m)
{
if(add[rt])
{
add[rt<<1]+=add[rt]; //lazytag的父子传递
add[rt<<1|1]+=add[rt];
if(add[rt]%3==1) { //每个结点的变化是由父节点的lazy tag所决定的。累加的和add[rt]%3==1即实现循环移一位
solve(rt<<1);solve(rt<<1|1);
}
if(add[rt]%3==2){ //同理循环移两位
solve2(rt<<1);solve2(rt<<1|1);
}
add[rt]=0;
}
}
void Build(int l,int r,int rt)
{
add[rt]=0;
one[rt]=0;
two[rt]=0;
if(l==r)
{
zero[rt]=1; //初始都为0,满足0%3==0.
return;
}
int m=(l+r)>>1;
Build(lson);
Build(rson);
PushUP(rt);
}
void Update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&R>=r)
{
add[rt]+=c; //lazytag先加上并且因为每次是加一的,故对当前区间进行循环一位移位。询问到时pushdown lazy tag
int temp=zero[rt];
zero[rt]=two[rt];
two[rt]=one[rt];
one[rt]=temp;
return;
}
PushDown(rt,r-l+1);
int m=(l+r)>>1;
if(L<=m)
Update(L,R,c,lson);
if(R>m)
Update(L,R,c,rson);
PushUP(rt);
}
LL Query(int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r)
return zero[rt];
PushDown(rt,r-l+1);
int m=(l+r)>>1;
LL ret=0;
if(L<=m) ret+=Query(L,R,lson);
if(R>m) ret+=Query(L,R,rson);
return ret;
}
int main(int argc, char** argv) {
int n,q;
scanf("%d%d",&n,&q);
Build(1,n,1);
while(q--){
int p,l,r;
scanf("%d%d%d",&p,&l,&r);
if(!p) Update(l,r,1,1,n,1);
else{
printf("%d\n",Query(l,r,1,n,1));
}
// for(int i=1;i<=n*2-1;i++) cout<<add[i]<<' ';cout<<endl;
// for(int i=1;i<=n*2-1;i++) cout<<"("<<one[i]<<','<<two[i]<<','<<zero[i]<<")"<<' '; cout<<endl; //树上每个结点的余1,2,0的总量统计情况
}
return 0;
}