言的幸运数
总提交 : 48 测试通过 : 7
描述
输入
第一行一个整数n(1<=n<=100000)和q(1<=q<=50000),表示有n个数q次操作;接下来q行每行对应一个操作'1 i j' 或者'2 i j'(0<=i,j<n),n 和q 都为0结束运行
输出
For each case, print the case number first.对每个询问输出结果,占一行。
样例输入
10 9
1 0 9
1 3 7
1 1 4
2 1 7
1 2 2
2 2 4
2 8 8
1 5 8
2 6 9
0 0
样例输出
Case 1:
2
3
0
2
这题还是挺经典的,典型的线段树,难点在于判断范围里面被3整除的树不能用for循环判断,铁定超时啊(哭泣……)
主要是发现一个规律,每次加1,被3除余一,余二,余0的数的数目恰好互换:
int temp=t[k][0];
t[k][0]=t[k][2];
t[k][2]=t[k][1];
t[k][1]=temp;
(t[k][0]代表线段树中下标为K的树中被3除余0的数的数目)
弄懂了这个就容易了,具体代码如下:
#include<stdio.h>
#include<iostream>
using namespace std;
#define ls k<<1
#define rs k<<1|1
const int MAX=100005;
int n,t[MAX<<2][3],flag[MAX<<2];
void push_up(int k)
{
t[k][0]=t[ls][0]+t[rs][0];
t[k][1]=t[ls][1]+t[rs][1];
t[k][2]=t[ls][2]+t[rs][2];
}
void build(int l,int r,int k)
{
flag[k]=0;
int mid=(l+r)>>1;
if(l==r)
{
t[k][0]=1;
t[k][1]=t[k][2]=0;
return ;
}
build(l,mid,ls);
build(mid+1,r,rs);
push_up(k);
}
void change(int k)
{
int temp=t[k][0];
t[k][0]=t[k][2];
t[k][2]=t[k][1];
t[k][1]=temp;
}
void update(int l,int r,int L,int R,int k)
{
// cout<<":"<<L<<" "<<R<<" "<<k<<endl;
if(l<=L&&r>=R)
{
flag[k]++;
change(k);
return ;
}
int mid=(L+R)>>1;
if(flag[k])
{
flag[ls]+=flag[k];
flag[rs]+=flag[k];
int temp=flag[k]%3;
while(temp--)
{
change(ls);
change(rs);
}
flag[k]=0;
}
if(l<=mid)
update(l,r,L,mid,ls);
if(r>=mid+1)
update(l,r,mid+1,R,rs);
push_up(k);
}
int query(int l,int r,int L,int R,int k)
{
int mid=(L+R)>>1;
if(l<=L&&r>=R)
{
// cout<<"haha\n";
return t[k][0];
}
if(flag[k])
{
flag[ls]+=flag[k];
flag[rs]+=flag[k];
int temp=flag[k]%3;
while(temp--)
{
change(ls);
change(rs);
}
flag[k]=0;
}
int ans=0;
if(l<=mid)
ans+=query(l,r,L,mid,ls);
if(r>=mid+1)
ans+=query(l,r,mid+1,R,rs);
return ans;
}
int main()
{
int q,i,j,x,y,z,p=0;
while(~scanf("%d%d",&n,&q)&&n+q)
{
printf("Case %d:\n",++p);
build(1,n,1);
for(i=0;i<q;i++)
{
scanf("%d%d%d",&x,&y,&z);
if(y>z)
swap(y,z);
if(x==1)
update(y+1,z+1,1,n,1);
else
cout<<query(y+1,z+1,1,n,1)<<endl;
}
}
return 0;
}