题目描述:
给你N个数,有两种操作
1:给区间[a,b]内的所有数都增加X
2:询问区间[a,b]能被7整除的个数
输入描述:
第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,表示操作的个数. 接下来Q行每行若干个整数。如果第一个数是add,后接3个正整数a,b,X,表示在区间[a,b]内每个数增加X,如果是count,表示统计区间[a,b]能被7整除的个数
输出描述:
对于每个询问输出一行一个答案
样例输入:
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
样例输出:
0
0
0
1
数据范围:1<=N,Q<=100000
题解:
很简单的线段树练习,给线段树的每一个节点开一个小数组g[i]表示这个节点表示的区间内的所有数对7取模的结果为i的数有多少个。然后就跟普通的线段树一样做咯。
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<queue>
#define LiangJiaJun main
using namespace std;
int n,q;
int a[100004];
struct data{
int g[10];
int l,r,tag;
}tr[400004];
void build(int k,int l,int r){
tr[k].l=l;tr[k].r=r;
if(l==r){
tr[k].g[a[l]]=1;return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
for(int i=0;i<7;i++)
tr[k].g[i]=tr[k<<1].g[i]+tr[k<<1|1].g[i];
}
void update(int k,int pd){
int s[10];
for(int i=0;i<7;i++)s[(i+pd)%7]=tr[k].g[i];
for(int i=0;i<7;i++)tr[k].g[i]=s[i];
}
void push(int k){
int pd=tr[k].tag%7;
update(k<<1,pd);update(k<<1|1,pd);
tr[k<<1].tag+=pd;
tr[k<<1|1].tag+=pd;
tr[k].tag=0;
}
void add(int k,int a,int b,int w){
int l=tr[k].l,r=tr[k].r;
if(l==a&&r==b){
tr[k].tag+=w;
update(k,w);
return;
}
if(tr[k].tag)push(k);
int mid=(l+r)>>1;
if(b<=mid)add(k<<1,a,b,w);
else if(a>mid)add(k<<1|1,a,b,w);
else {
add(k<<1,a,mid,w);add(k<<1|1,mid+1,b,w);
}
for(int i=0;i<7;i++)
tr[k].g[i]=tr[k<<1].g[i]+tr[k<<1|1].g[i];
}
int query(int k,int a,int b){
int l=tr[k].l,r=tr[k].r;
if(l==a&&r==b)return tr[k].g[0];
if(tr[k].tag)push(k);
int mid=(l+r)>>1;
if(b<=mid)return query(k<<1,a,b);
else if(a>mid)return query(k<<1|1,a,b);
else{
return (query(k<<1,a,mid)+query(k<<1|1,mid+1,b));
}
}
int LiangJiaJun(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),a[i]%=7;
build(1,1,n);
scanf("%d",&q);
while(q--){
char ch[14];
int x,y,z;
scanf("%s",ch);
if(ch[0]=='a'){
scanf("%d%d%d",&x,&y,&z);
z%=7;if(z==0)continue;
add(1,x,y,z);
}
else{
scanf("%d%d",&x,&y);
printf("%d\n",query(1,x,y));
}
}
return 0;
}