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
这道题的关键点在于第二种操作,也就是询问[l,r]间购买鸡排数为3的倍数的用户数。
第一次提交TLE了,原因在于我是遍历[l,r]间的购买鸡排数。
这道题是我校EC-Final选拔赛的一道题,也正因为我的队友在最后时间做出来了这道题,我们才侥幸可以参加EC-Final。
在我TLE了之后,我偷偷地爬上队友的账号,才恍然大悟!
数组里放数据应该是鸡排数为3的倍数的用户数,而不是鸡排数。
AC的代码如下:
1
0
2
这道题的关键点在于第二种操作,也就是询问[l,r]间购买鸡排数为3的倍数的用户数。
第一次提交TLE了,原因在于我是遍历[l,r]间的购买鸡排数。
这道题是我校EC-Final选拔赛的一道题,也正因为我的队友在最后时间做出来了这道题,我们才侥幸可以参加EC-Final。
在我TLE了之后,我偷偷地爬上队友的账号,才恍然大悟!
数组里放数据应该是鸡排数为3的倍数的用户数,而不是鸡排数。
AC的代码如下:
#include<iostream>
#include<algorithm>
#include<cstdio>
int const Maxn=100000;
using namespace std;
struct tree
{
int left,right,add,y0,y1,y2;
}t[Maxn<<2]; // 线段树的空间大概是数组空间的4倍
void Pushup(int i) //更新父节点的值
{
t[i].y0=t[i<<1].y0+t[(i<<1)+1].y0;
t[i].y1=t[i<<1].y1+t[(i<<1)+1].y1;
t[i].y2=t[i<<1].y2+t[(i<<1)+1].y2;
}
void Build(int i,int l,int r)
{
t[i].y0=0;
t[i].y1=0;
t[i].y2=0;
t[i].left=l;
t[i].right=r;
t[i].add=0;
if(l==r) //由题意0也是3的倍数
{
t[i].y0=1;
return;
}
else
{
int mid=(l+r)/2;
Build(i<<1,l,mid);
Build((i<<1)+1,mid+1,r);
Pushup(i);
}
}
void Pushdown(int i) // 向下更新两个子节点的值
{
int n=t[i].add;
if(n){
n=n%3;
while(n--){
int tmp=i<<1;
int t0=t[tmp].y0;
int t1=t[tmp].y1;
int t2=t[tmp].y2;
t[tmp].y0=t2;
t[tmp].y1=t0;
t[tmp].y2=t1;
t[tmp].add++;
t0=t[tmp+1].y0;
t1=t[tmp+1].y1;
t2=t[tmp+1].y2;
t[tmp+1].y0=t2;
t[tmp+1].y1=t0;
t[tmp+1].y2=t1;
t[tmp+1].add++;
}
}
t[i].add=0;
}
void Update(int i,int l,int r)
{
if(l==t[i].left&&r==t[i].right) // 如果区间完全重合,暂时不更新子节点的值,先记录需要更新的值。
{
int t0=t[i].y0;
int t1=t[i].y1;
int t2=t[i].y2;
t[i].y0=t2;
t[i].y1=t0;
t[i].y2=t1;
t[i].add++;
return;
}
if(t[i].add)
Pushdown(i);
int tmp=i<<1;
int mid=(t[i].left+t[i].right)>>1;
if(r<=mid) // 要求的左右边界全部在当前区间的左半部分
Update(tmp,l,r);
else if(l>mid) //要求的左右边界全部在当前区间的右半部分
Update(tmp+1,l,r);
else //要求的左右边界在当前区间的左右两半部分都有涉及,要切分开来计算
{
Update(tmp,l,mid);
Update(tmp+1,mid+1,r);
}
Pushup(i);
}
int Query(int i,int l,int r) // 查询区间l到r
{
if(t[i].left==l&&t[i].right==r)
{
return t[i].y0;
}
if(t[i].add)
Pushdown(i);
int mid=(t[i].left+t[i].right)>>1;
if(r<=mid)
return Query(2*i,l,r);
else if(l>mid)
return Query(2*i+1,l,r);
else
{
return Query(2*i,l,mid)+Query(2*i+1,mid+1,r);
}
}
int main(void)
{
int n,q,t,l,r;
scanf("%d%d",&n,&q);
Build(1,1,n);
while(q--)
{
scanf("%d%d%d",&t,&l,&r);
if(t==0)
Update(1,l,r);
else
{
printf("%d\n",Query(1,l,r));
}
}
return 0;
}