POJ 3468 区间修改+区间求和 (树状数组/线段树)

本文介绍了解决区间修改和区间求和问题的两种方法:线段树和树状数组。通过实例解析,展示了如何使用这两种数据结构高效处理大量区间操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

~by Wjvje 2019-4-30

 

题目链接:http://poj.org/problem?id=3468

题目描述:

                                                              A Simple Problem with Integers

Time Limit: 5000MS Memory Limit: 131072K
Total Submissions: 155314 Accepted: 48062
Case Time Limit: 2000MS

Description

You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of AaAa+1, ... , Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

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

Sample Output

4
55
9
15

Hint

The sums may exceed the range of 32-bit integers.

题意:给出一段区间,两种操作,区间修改或者区间求和,数据范围如上。

思路:区间求和+区间修改,线段树/树状数组均可:

      线段树的加个lazy标记;

      树状数组:因为有区间修改,所以树状数组的话加上差分;

又因为树状数组维护的是单个元素的增量数组,所以对于树状

数组做带有区间修改的区间查询要根据数学推导得出最终的求

解公式,百度一下有关内容挺详细的,然后最后根据推导出来

的式子维护两个树状数组即可。

线段树代码:(区间修改lazy标记)

// 求和、数组啥的改成LL ,懒得改了
/**Wjvje**/
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int N=4*2e5+100;
const int M=4e5+100;
using namespace std;
int a[N];
int dat[N],Left[N],Right[N];
int flag[N];
void build(int p,int l,int r)//只有建树时递归区间才会变; 
{
	Left[p]=l;
	Right[p]=r;
	if(l==r)
	{
		dat[p]=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(2*p,l,mid);
	build(2*p+1,mid+1,r);
	dat[p]=dat[2*p]+dat[2*p+1];
}
void spread(int p)
{
	if(!flag[p])return ;
	dat[2*p]+=flag[p]*(Right[2*p]-Left[p*2]+1);
	dat[2*p+1]+=flag[p]*(Right[2*p+1]-Left[p*2+1]+1);
	flag[2*p]+=flag[p];
	flag[2*p+1]+=flag[p];
	flag[p]=0;
}
void change(int p,int l,int r,int v)//递归时,查询区间是L,R时不变的; 
{
	if(l<=Left[p]&&r>=Right[p])
	{
		dat[p]+=v*(Right[p]-Left[p]+1);
		flag[p]+=v;
		return ;
	}
	int mid=(Left[p]+Right[p])>>1;
	spread(p);
	if(l<=mid)change(p*2,l,r,v);
	if(r>mid)change(2*p+1,l,r,v);
	dat[p]=dat[p*2]+dat[2*p+1];
}
int ask(int p,int l,int r)//递归时,查询区间是L,R时不变的; 
{
	if(l<=Left[p]&&r>=Right[p])return dat[p];
	int mid=(Left[p]+Right[p])>>1;
	LL val=0;
	spread(p);
	if(l<=mid)val+=ask(p*2,l,r);
	if(r>mid)val+=ask(2*p+1,l,r);
	return val;
}
int main()
{
	io;
	int n,m;
	while(cin>>n>>m)
	{
		memset(dat,0,sizeof(dat));
		for(int i=1;i<=n;++i)cin>>a[i];
		build(1,1,n);
		while(m--)
		{
			char ch;
			int x,y;
			int z;
			cin>>ch>>x>>y;
			if(ch=='Q')cout<<ask(1,x,y)<<endl;
			else cin>>z,change(1,x,y,z);
		}
	}
	return 0;
}

树状数组(区间修改(差分)+区间求和(数学化简))代码:

/**Wjvje**/
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int N=2e5+100;
using namespace std;
LL a[N],sum[N],c[N],d[N];//c、d两个不同的前缀和 
int n;
LL find(int x,LL *array)
{
	LL res=0;
	for(;x;x-=x&-x)res+=array[x];
	return res;
}
void add(int x,int v,LL *array)
{
	for(;x<=n;x+=x&-x)array[x]+=v;
}
int main()
{
	io; 
	int m;
	while(cin>>n>>m)
	{
		memset(sum,0,sizeof(sum));
		memset(c,0,sizeof(c));
		memset(d,0,sizeof(d));
		for(int i=1;i<=n;++i)cin>>a[i];
		for(int i=1;i<=n;++i)sum[i]=sum[i-1]+a[i];
		while(m--)
		{
			int x,y,z;
			char ch;
			cin>>ch;
			if(ch=='Q'){
				cin>>x>>y;
				x=x-1;
				LL sum2=sum[y]+(y+1)*find(y,c)-find(y,d);//公式求解 
				LL sum1=sum[x]+(x+1)*find(x,c)-find(x,d);
				cout<<sum2-sum1<<endl;
			}
			else{
				cin>>x>>y>>z;     //树状数组区间修改, 差分法,x位置加,y+1位置减 
				add(x,z,c);           //维护两个关于b【】增量数组的前缀和 
				add(y+1,-z,c);
				add(x,(LL)x*z,d);
				add(y+1,-(LL)(y+1)*z,d);
			}
		}	
	}
	return 0;
} 

 

 

The end;

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值