POJ3468 A Simple Problem with Integers

题目大意:线段树区间加减,区间求和。

时间限制:5000ms

进行了位运算和输入优化,用时1469ms

分析:

build函数是建树,边界时把数据输到add数组里,add数组也就是常说的lazy标记,再把add的值赋给sum,建树完毕。

用sum[o]表示“如果只执行结点o及其子孙结点中的add操作,结点o对应区间中的所有数之和”,这样可以方便维护。

在执行update操作时,递归访问到的结点全部都要维护,并且是在递归返回后维护,因为这样子树的sum已经更新。

查询操作多加一个参数,表示当前区间的所有祖先结点add值之和,用全局变量sumv表示答案。

接下来是一些位运算优化,自行看代码。

数组开2.5倍区间长度会爆,开3倍不会。

数据类型用long long.

#include <iostream>
#include <cstdio>
using namespace std;

typedef long long LL;
char c;
int n, q, x, y, z;
LL sumv, sum[300005], add[300005];

void build(int o, int L, int R) {
	if(L == R) {
		scanf("%lld", &add[o]);
		sum[o] = add[o];
		return;
	}
	int m = (L+R) >> 1;
	build(o<<1, L, m);
	build(o<<1 | 1, m+1, R);
	sum[o] = sum[o<<1] + sum[o<<1 | 1];
}

void maintain(int o, int L, int R) {
	sum[o] = 0;
	if(R > L) sum[o] = sum[o<<1] + sum[o<<1 | 1];
	sum[o] += add[o] * (R-L+1);
}

void update(int o, int L, int R) {
	if(x <= L && y >= R)
		add[o] += z;
	else {
		int m = (L+R) >> 1;
		if(x <= m) update(o<<1, L, m);
		if(y > m) update(o<<1 | 1, m+1, R);
	}
	maintain(o, L, R);
}

void query(int o, int L, int R, LL addv) {
	if(x <= L && y >= R)
		sumv += sum[o] + addv * (R-L+1);
	else {
		int m = (L+R) >> 1;
		if(x <= m) query(o<<1, L, m, addv+add[o]);
		if(y > m) query(o<<1 | 1, m+1, R, addv+add[o]);
	}
}

int main() {
	scanf("%d%d", &n, &q);
	build(1, 1, n);
	while(q--) {
		cin>>c;
		if(c == 'Q') {
			scanf("%d%d", &x, &y);
			sumv = 0;
			query(1, 1, n, 0);
			printf("%lld\n", sumv);
		}
		else {
			scanf("%d%d%d", &x, &y, &z);
			update(1, 1, n);
		}
	}
	return 0;
}



你可能感兴趣的:(线段树,位运算优化)