输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
输出样例:
4
55
9
15
树状数组做法
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
const int N=200010;
ll a[N],c[2][N],sum[N],n,m;
ll ask(int k,int x){
ll ans=0;
for(;x;x-=lowbit(x)) ans+=c[k][x];
return ans;
}
void add(int k,int x,int y){
for(;x<=n;x+=lowbit(x)) c[k][x]+=y;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i],sum[i]=sum[i-1]+a[i];
while(m--){
char ch;
cin>>ch;
if(ch=='C'){
int l,r,d;
cin>>l>>r>>d;
add(0,l,d);
add(0,r+1,-d);
add(1,l,l*d);
add(1,r+1,-d*(r+1));
}
else{
int x,y;
cin>>x>>y;
ll res=sum[y]+(y+1)*ask(0,y)-ask(1,y) - (sum[x-1]+x*ask(0,x-1)-ask(1,x-1));
cout<<res<<endl;
}
}
return 0;
}
作者:拓海
链接:https://www.acwing.com/activity/content/code/content/1763390/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
线段树做法
线段树每个节点保存 l 、r、sum(区间和) 、add(增量延迟标记)
spread 函数实现了延迟标记的向下传递
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
int a[N],n,m;
struct ST{
int l,r;
ll sum,add;
}t[N*4];
void build(int p,int l,int r){
t[p].l = l,t[p].r = r;//p 节点代表区间[l,r]
if(l==r) {
t[p].sum = a[l];return ;//到叶节点
}
int mid = (l+r)/2;//折半
build(p*2,l,mid); //左子节点[l,mid] 编号2*p
build(p*2+1,mid+1,r); //右子节点[mid+1,r] 编号2*p+1
t[p].sum=t[p*2].sum + t[p*2+1].sum;
}
void spread(int p){
if(t[p].add){
t[p*2].sum += t[p].add*(t[p*2].r - t[p*2].l+1);//更新左子节点
t[p*2+1].sum += t[p].add*(t[p*2+1].r - t[p*2+1].l+1);//更新右子节点
t[p*2].add += t[p].add;//给左子节点打延迟标记
t[p*2+1].add += t[p].add;//给右子节点打延迟标记
t[p].add = 0;//清除节点p的标记
}
}
void change(int p,int l,int r,int d){
if( l<=t[p].l && r>=t[p].r){ //完全覆盖
t[p].sum += (ll)d*(t[p].r-t[p].l+1); //更新节点信息
t[p].add += d; //给节点打延迟标记
return ;
}
spread(p);
int mid=(t[p].l + t[p].r)/2;
if(l<=mid) change(p*2, l, r, d);// x 属于左半区间
if(r>mid) change(p*2+1, l, r, d); // x 属于右半区间
t[p].sum = t[p*2].sum + t[p*2+1].sum;
}
ll ask(int p,int l,int r){
if(l<=t[p].l && r>=t[p].r) return t[p].sum;//完全包含
spread(p);
int mid = (t[p].l + t[p].r) / 2;
ll val = 0; //负无穷大
if(l<=mid) val += ask(p*2,l,r);// 左子节点有重叠
if(r>mid) val += ask(p*2+1,l,r);// 右子节点有重叠
return val;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
while(m--){
char ch;
int l,r,d;
cin>>ch>>l>>r;
if(ch=='C'){
cin>>d;
change(1,l,r,d);
}
else cout<<ask(1,l,r)<<endl;
}
return 0;
}
分块做法
#include<bits/stdc++.h>
#define ll long long
#define ini(a) memset(a,0,sizeof(a))
#define ini2(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=100010;
ll a[N],sum[N],add[N];
int L[N],R[N];
int pos[N];
int n,m,t;
void change(ll l, ll r, ll d) {
ll p = pos[l], q = pos[r];
if (p == q) {
for (ll i=l; i<=r; i++) a[i]+=d;
sum[p] += d*(r-l+1);
} else {
//和上面类似
for (ll i=l; i<=R[p]; i++) a[i] += d;
sum[p] += d*(R[p]-l+1);
for (ll i=L[q]; i<=r; i++) a[i] += d;
sum[q] += d*(r-L[q]+1);
for (ll i=p+1; i<=q-1; i++) add[i] += d;
}
}
ll ask(ll l, ll r) {
ll ans = 0;
ll p = pos[l], q = pos[r];
if (p == q) {
for (ll i=l; i<=r; i++) {
ans += a[i];
}
ans += add[q]*(r-l+1);
} else {
for (ll i=l; i<=R[p]; i++) ans += a[i];
ans += add[p]*(R[p]-l+1);
for (ll i=L[q]; i<=r; i++) ans += a[i];
ans += add[q]*(r-L[q]+1);
for (ll i=p+1; i<=q-1; i++) {
ans += add[i] * (R[i]-L[i]+1);
ans += sum[i];
}
}
return ans;
}
void solve() {
cin>>n>>m;
for (ll i=1; i<=n; i++) cin>>a[i];
//分块
t = sqrt(n);
for (ll i=1; i<=t; i++) {
L[i] = (i-1)*t+1;
R[i] = i*t;
}
if (R[t]<n) t++, L[t] = R[t-1]+1, R[t] = n;//最后一段处理
//预处理
ini(sum), ini(add);
for (ll i=1; i<=t; i++) {
for (ll j=L[i]; j<=R[i]; j++) {
pos[j] = i;//属于第几段
sum[i] += a[j];
}
}
//指令
for (ll i=1; i<=m; i++) {
char ch;
ll l, r, d;
cin>>ch>>l>>r;
if (ch == 'C') {
cin>>d;
change(l, r, d);
} else {
cout<<ask(l, r)<<endl;
}
}
}
int main()
{
solve();
return 0;
}