【codevs4919】线段树练习4

时间:2023-03-10 06:44:02
【codevs4919】线段树练习4

题目大意:维护一个长度为 N 的序列,支持两种操作:区间加,区间查询有多少数是 7 的倍数。

题解:在每个线段树中维护一个权值数组 [0,6],由于个数显然支持区间可加性,因此可用线段树来维护。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10; inline int read(){
int x=0,f=1;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
return f*x;
} int n,q,a[maxn],tmp[7];//处理循环需要额外开一个数组
char opt[10]; struct node{int lc,rc,tag,cnt[7];};
struct segment_tree{
#define ls t[k].lc
#define rs t[k].rc
node t[maxn<<1];
int tot;
segment_tree():tot(1){
memset(t,0,sizeof(t));
}
inline void pushup(int k){
for(int i=0;i<7;i++)t[k].cnt[i]=t[ls].cnt[i]+t[rs].cnt[i];
}
inline void cycle(int k,int val){
for(int i=0;i<7;i++)tmp[i]=t[k].cnt[i];
for(int i=0;i<7;i++)t[k].cnt[(i+val)%7]=tmp[i];
}
inline void pushdown(int k){
cycle(ls,t[k].tag),cycle(rs,t[k].tag);
t[ls].tag=(t[ls].tag+t[k].tag)%7;
t[rs].tag=(t[rs].tag+t[k].tag)%7;
t[k].tag=0;
}
void build(int k,int l,int r){
if(l==r){t[k].cnt[a[l]%7]++;return;}
int mid=l+r>>1;
ls=++tot,build(ls,l,mid);
rs=++tot,build(rs,mid+1,r);
pushup(k);
}
void modify(int k,int l,int r,int x,int y,int val){
if(l==x&&r==y){
cycle(k,val);
t[k].tag=(t[k].tag+val)%7;
return;
}
int mid=l+r>>1;
pushdown(k);
if(y<=mid)modify(ls,l,mid,x,y,val);
else if(x>mid)modify(rs,mid+1,r,x,y,val);
else modify(ls,l,mid,x,mid,val),modify(rs,mid+1,r,mid+1,y,val);
pushup(k);
}
int query(int k,int l,int r,int x,int y){
if(l==x&&r==y)return t[k].cnt[0];
int mid=l+r>>1;
pushdown(k);
if(y<=mid)return query(ls,l,mid,x,y);
else if(x>mid)return query(rs,mid+1,r,x,y);
else return query(ls,l,mid,x,mid)+query(rs,mid+1,r,mid+1,y);
}
}sgt; void read_and_parse(){
n=read();
for(int i=1;i<=n;i++)a[i]=read();
sgt.build(1,1,n);
q=read();
} void solve(){
int l,r,val;
while(q--){
scanf("%s",opt);
if(opt[0]=='c'){
l=read(),r=read();
printf("%d\n",sgt.query(1,1,n,l,r));
}else{
l=read(),r=read(),val=read();
sgt.modify(1,1,n,l,r,val);
}
}
} int main(){
read_and_parse();
solve();
return 0;
}