树状数组、线段树、分块 在同一题目中的应用(Acwing 243)

Acwing 243
在这里插入图片描述

输入样例:

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值