POJ-3468 A Simple Problem with Integers

分块

A Simple Problem with Integers

给出了一个序列,你需要处理如下两种询问。"C a b c"表示给[a, b]区间中的值全部增加c (-10000 ≤ c ≤ 10000)。"Q a b" 询问[a, b]区间中所有值的和。

Input:

第一行包含两个整数N, Q。1 ≤ N,Q ≤ 100000.

第二行包含n个整数,表示初始的序列A (-1000000000 ≤ Ai ≤ 1000000000)。

接下来Q行询问,格式如题目描述。

Output

对于每一个Q开头的询问,你需要输出相应的答案,每个答案一行。

思路:

由于是对区间进行操作,若每次操作都修改每个元素的值则必定超时.故我们可以将这个数组分成几块,每次对完整的块就对整个块进行操作,两边残缺的块一个个操作即可.更新时相似,对于完整的块直接计入答案,再一个个加上两边残缺的块.每次只用维护每个块的和即可(部分注意事项见代码).

AC代码:

#include<cstdio>
#include<cmath>
char s[4];
int A[100005];
int S,ADD[320];
long long sum[320];
long long Read() {//读入优化对于POJ的题目作用挺大的
	char c;
	int flag=1;
	long long res=0ll;
	while(c=getchar(),c<48) {
		if(c=='-')flag=-1;
	}
	do res=1ll*(res<<3)+(res<<1)+(c^48);
	while(c=getchar(),c>=48);
	res*=flag;
	return res;
}
void add(int l,int r,int d) {
	int ka=l/S,kb=r/S;//ka,kb为块编号.
	if(ka==kb){
		for(int i=l;i<=r;i++)A[i]+=d;
		sum[ka]+=1ll*(r-l+1)*d;
		return;
	}
	for(int i=l; i<(ka+1)*S; i++)A[i]+=d,sum[ka]+=1ll*d;//前面残缺的块
	for(int i=kb*S; i<=r; i++)A[i]+=d,sum[kb]+=1ll*d;//后面残缺的块
	for(int i=ka+1; i<kb; i++)ADD[i]+=d,sum[i]+=1ll*d*S;//中间完整的块
    //此处的ADD为了之后查询到一个块中间的一部分的值
}
long long find(int l,int r) {
	int ka=l/S,kb=r/S;
	long long ans=0ll;
	if(ka==kb) {
		for(int i=l; i<=r; i++)ans+=1ll*A[i]+ADD[ka];//加上这个块的增值
		return ans;
	}
	for(int i=l; i<(ka+1)*S; i++)ans+=1ll*A[i]+ADD[ka];//前面残缺的块
	for(int i=kb*S; i<=r; i++)ans+=1ll*A[i]+ADD[kb];//后面残缺的块
	for(int i=ka+1; i<kb; i++)ans+=1ll*sum[i];//中间完整的块
	return ans;
}
int main() {
	int n,q;
	n=Read();
	q=Read();
	S=sqrt(n);
	for(int i=1; i<=n; i++)A[i]=Read(),sum[i/S]+=1ll*A[i];//初始化块的和
	while(q--) {
		scanf("%s",s);
		if(s[0]=='C') {
			int a,b,d;
			a=Read();
			b=Read();
			d=Read();
			add(a,b,d);
		} else {
			int l,r;
			l=Read();
			r=Read();
			printf("%lld\n",find(l,r));
		}
	}
}

注意:前面第一个块一定是不完整的.

猜你喜欢

转载自blog.csdn.net/qq_35320178/article/details/81072481