线段树练习4
codevs题号: 4919
时间限制: 1 s
空间限制: 128000 KB
题目描述 Description:
给你N个数,有两种操作
1:给区间[a,b]内的所有数都增加X
2:询问区间[a,b]能被7整除的个数
输入描述 Input Description
第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,表示操作的个数. 接下来Q行每行若干个整数。如果第一个数是add,后接3个正整数a,b,X,表示在区间[a,b]内每个数增加X,如果是count,表示统计区间[a,b]能被7整除的个数
输出描述 Output Description
对于每个询问输出一行一个答案
样例输入 Sample Input
3
2 3 4
6
count 1 3
count 1 2
add 1 3 2
count 1 3
add 1 3 3
count 1 3
样例输出 Sample Output
0
0
0
1
数据范围及提示 Data Size & Hint
100%:1< N<= 100000,1< Q<= 100000
题解:
比较水啊这题,我们可以在线段树的结构体数组中加上一个数组g[i]表示这个区间中与7取余为i的数有多少个。每次更新时都更新这个数组的值就好了,具体看代码。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long int a[200010],add[800010];
int n,m,x,y,v,pd,num=0;
struct hh
{
int f,l,r,ls,rs;//ls为左子节点,rs为右子节点
long long int date;
int g[10];//g[i]保存 此区间中和7取余为i的数右多少个
}t[800010];
char pdd[10];
void build(int l,int r)
{
num++;
int i=num;
for(int j=0;j<7;j++)
t[i].g[j]=0;//手动初始化
t[i].l=l;t[i].r=r;
if(l!=r)
{
t[i].ls=num+1;
build(l,(l+r)/2);//建立左子节点
t[i].rs=num+1;
build(((l+r)/2+1),r);//建立右子节点
for(int j=0;j<7;j++)//父节点的g数组的值就是子节点的g数组的和
{
t[i].g[j]=t[t[i].ls].g[j]+t[t[i].rs].g[j];
}
}
else if(l==r)
{
t[i].g[(a[l]%7)]=1;//叶子节点 直接赋值
}
}
void pushdown(int i)//更新子节点的值
{
if(add[i]!=0)
{
int s[10],va=add[i];//利用一个s数组,保存更新后的值
for(int j=0;j<7;j++)
{
s[(j+va)%7]=t[t[i].ls].g[j];//左儿子更新数据
}
for(int j=0;j<7;j++)//再把更新后数据赋给节点的g数组
t[t[i].ls].g[j]=s[j];
for(int j=0;j<7;j++)
{
s[(j+va)%7]=t[t[i].rs].g[j];//右儿子更新数据
}
for(int j=0;j<7;j++)//把更新后数据赋给节点的g数组
t[t[i].rs].g[j]=s[j];
add[t[i].ls]+=va;
add[t[i].rs]+=va;
add[i]=0;
}
}
void update(int i,int l,int r,int value)
{
if(t[i].l==l&&t[i].r==r)//完全覆盖,直接算
{
add[i]+=value;
int s[10];
for(int j=0;j<7;j++)
{
s[(j+value)%7]=t[i].g[j];//利用一个s数组,保存更新后的值
}
for(int j=0;j<7;j++)//再把更新后数据赋给节点的g数组
t[i].g[j]=s[j];
return ;
}
if(t[i].l==t[i].r) return ;
pushdown(i);//把延迟更新的值传给子节点
int z=(t[i].l+t[i].r)/2;//区间更新的套路啦
if(r<=z) update(t[i].ls,l,r,value);
else if(l>z) update(t[i].rs,l,r,value);
else
{
update(t[i].ls,l,z,value);
update(t[i].rs,z+1,r,value);
}
for(int j=0;j<7;j++)//父节点的g数组的值就是子节点的g数组的和
{
t[i].g[j]=t[t[i].ls].g[j]+t[t[i].rs].g[j];
}
}
long long int query(int i,int l,int r)//求和
{
if(t[i].l==l&&t[i].r==r)//完全覆盖,直接返回值
{
return t[i].g[0];
}
pushdown(i);//降延迟更新的值传给儿子节点
int z=(t[i].l+t[i].r)/2;//还是线段树区间更新的套路
if(r<=z) return query(t[i].ls,l,r);
else if(l>z) return query(t[i].rs,l,r);
else return query(t[i].ls,l,z)+query(t[i].rs,z+1,r);
}
int main()
{
memset(add,0,sizeof(add));
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
a[i]%=7; //预处理先 mod7
}
build(1,n);
cin>>m;
for(int i=1;i<=m;i++)
{
scanf("%s",pdd);
{
if(pdd[0]=='a')
{
scanf("%d%d%d",&x,&y,&v);
if((v%=7)!=0)//要加上的值如果和mod7为0的话就没什么用了,不必更新。
update(1,x,y,v%7);
}
else if(pdd[0]=='c')
{
scanf("%d%d",&x,&y);
cout<<query(1,x,y)<<endl;
}
}
}
return 0;
}