`
kenby
  • 浏览: 717160 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

poj 3468 非递归的zkw式线段树解法

 
阅读更多

关于zkw式线段树,请参考zkw大牛的ppt--百度文库《统计的力量》,其特点是利用完全二叉树数组存储的特点,自底向上遍历节点,非递归地完成查询和更新,加上位操作的使用,可以提高程序的效率。ps:__int64比long long的效率高很多。

#include <stdio.h>

//#define DEBUG

#ifdef DEBUG
#define debug(...) printf( __VA_ARGS__) 
#else
#define debug(...)
#endif

#define N 100002

struct tree_node{
	int s;
	int t;
	long long sum;	/* A[s]+...+A[t]的部分和 */
	long long d;	/* A[s]...A[t]的公共增量 */
};

struct tree_node tree[262144];

int A[N];
int M;				/* M表示线段树中叶子节点的个数, 对于完全二叉树,M是2的n次幂 */

/* 对[0,M-1]建造线段树,M是2的n次幂,保证了这是一棵完全二叉树 */
void make_tree(int s, int t)
{
	int		i;

	for (i = 2*M-1; i > 0; i--) {
		if (i >= M) {	/* 叶子节点 */
			tree[i].sum = A[i-M];
			tree[i].s = tree[i].t = (i-M);
		}
		else {			/* 分支节点 */
			tree[i].sum = tree[i<<1].sum + tree[(i<<1)+1].sum;
			tree[i].s = tree[i<<1].s;
			tree[i].t = tree[(i<<1)+1].t;
		}
	}
}

/* 区间(s,t)的和等于线段<s,t>的所有匹配线段之和,加上每条匹配线段的累计增量之和 */
long long query(int s, int t)
{
	long long 	sum;
	int 		num_s, num_t;	/* 被公共增量影响的元素个数 */

	sum = num_s = num_t = 0;

	for (s = s+M-1, t = t+M+1; (s^t) != 1;) {
		//加上匹配线段的和
		if (~s&1) {
			sum += tree[s^1].sum;
			num_s += (tree[s^1].t-tree[s^1].s+1);
		}
		if (t&1) {
			sum += tree[t^1].sum;
			num_t += (tree[t^1].t-tree[t^1].s+1);
		}

		//加上累计增量
		s >>= 1; t >>= 1;
		sum += num_s*tree[s].d;
		sum += num_t*tree[t].d;
	}

	//至此,s和t拥有共同的父亲,从它们的父亲开始往上继续加上累计增量,直至根节点
	for (s >>= 1;s > 0; s >>= 1) {
		sum += (num_s+num_t)*tree[s].d;
	}

	return sum;
}

/* 除了改变匹配线段的公共增量外,还要改变所有覆盖了匹配线段的线段的sum值 */
void add(int s, int t, int d)
{
	int 	num_s, num_t;

	num_s = num_t = 0;

	for (s = s+M-1, t = t+M+1; (s^t) != 1;) {
		//更新匹配线段的公共增量和sum
		if (~s&1) {
			tree[s^1].d += d;
			tree[s^1].sum += (tree[s^1].t-tree[s^1].s+1)*d;
			num_s += (tree[s^1].t-tree[s^1].s+1);
		}
		if (t&1) {
			tree[t^1].d += d;
			tree[t^1].sum += (tree[t^1].t-tree[t^1].s+1)*d;
			num_t += (tree[t^1].t-tree[t^1].s+1);
		}

		s >>= 1; t >>= 1;
		tree[s].sum += num_s*d;
		tree[t].sum += num_t*d;
	}

	for (s >>= 1; s > 0; s >>= 1) {
		tree[s].sum += (num_s+num_t)*d; 
	}
}

int main() 
{
	int 		n, q, i, s, t, value;
	long long 	sum;
	char		action;

	scanf("%d %d", &n, &q);
	for (i = 1; i <= n; i++) {
		scanf("%d", A+i);
	}

	/* M表示叶子节点的个数,为了建造完全二叉树,M必须是2的n次幂
	 * 树的第一个和最后一个叶子节点不能使用,所以叶子
	 * 节点至少要有n+2个,综上所述,M是大于等于(n+2)且可以
	 * 表示为2的n次幂的最小整数
	 */
	for (M = 1; M < (n+2); M <<= 1);
	make_tree(0, M-1);

	while (q--) {
		getchar();
		scanf("%c %d %d", &action, &s, &t);
		if (action == 'Q') {
			sum = query(s, t);
			printf("%lld\n", sum);
		}
		else {
			scanf("%d", &value);
			if (value != 0) {
				add(s, t, value);
			}
		}
	}
	return 0;
}
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics