码迷,mamicode.com
首页 > 其他好文 > 详细

奇偶线段树(区间更新)

时间:2015-08-14 21:30:18      阅读:126      评论:0      收藏:0      [点我收藏+]

标签:区间更新

题目链接:http://ccnu.acmclub.com/index.php?app=problem_title&id=613&problem_id=23875


题意:给你一个长度为n的数组(下标从1开始)。进行如下操作。

(1)1 x y v :表示将下标=(x,x+2,x+4,x+6,.......并且<=y)的元素全部+v;

(2)2 x y :查询[x,y]闭区间的元素和。


思路:构造2棵线段树。将区间[1,3,...,2k - 1]改为[1,2,...,k],用一棵线段树统计这个区间内的和。将区间[2,4,...,2k]改为[1,2,...,k],用一棵线段树统计这个区间内的和。剩下为普通的区间更新即可。


代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>

using namespace std;

#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define ceil(x, y) (((x) + (y) - 1) / (y))

const int N = 1e5 + 10;
const int INF = 0x7f7f7f7f;

struct Node {
	long long sum;
	long long add;
};
Node node[3][N << 2];//0 偶线段树 1 奇线段树
long long a[N];

void pushup(int op, int rt) {
	node[op][rt].sum = node[op][rt << 1].sum + node[op][rt << 1 | 1].sum;
}

void pushdown(int op, int rt, int len) {
	int lenl = (len - (len >> 1));
	int lenr = (len >> 1);
	if (node[op][rt].add) {
		node[op][rt << 1].sum += 1LL * lenl * node[op][rt].add;
		node[op][rt << 1 | 1].sum += 1LL * lenr * node[op][rt].add;
		node[op][rt << 1].add += node[op][rt].add;
		node[op][rt << 1 | 1].add += node[op][rt].add;
		node[op][rt].add = 0;
	}
}

void build(int op, int l, int r, int rt) {
	node[op][rt].add = 0;
	if (l == r) {
		if (op)
			node[op][rt].sum = a[2 * l - 1];
		else
			node[op][rt].sum = a[2 * l];
		return ;
	}
	int m = (l + r) >> 1;
	build(op, lson);
	build(op, rson);
	pushup(op, rt);
}

void update(int op, int L, int R, long long c, int l, int r, int rt) {
	if (L <= l && r <= R) {
		node[op][rt].sum += 1LL * (r - l + 1) * c;
		node[op][rt].add += c;
		return ;
	}
	pushdown(op, rt, r - l + 1);
	int m = (l + r) >> 1;
	if (L <= m)
		update(op, L, R, c, lson);
	if (R > m)
		update(op, L, R, c, rson);
	pushup(op, rt);
}

long long query(int op, int L, int R, int l, int r, int rt) {
	if (L <= l && r <= R) {
		return node[op][rt].sum;
	}
	pushdown(op, rt, r - l + 1);
	int m = (l + r) >> 1;
	long long ql = 0, qr = 0;
	if (L <= m)
		ql = query(op, L, R, lson);
	if (R > m)
		qr = query(op, L, R, rson);
	pushup(op, rt);
	return ql + qr;
}

int main() {	
	int t_case;
	scanf("%d", &t_case);
	for (int i_case = 1; i_case <= t_case; i_case++) {
		int n, m;
		int no, ne;
		bool flag = false;
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++) 
			scanf("%lld", &a[i]);
		no = ceil(n, 2);
		ne = n - no;
		build(1, 1, no, 1);
		if (ne) {//是否需要建偶线段树
			build(0, 1, ne, 1);
			flag = true;
		}
		for (int i_q = 1; i_q <= m; i_q++) {
			int op, l, r;
			scanf("%d%d%d", &op, &l, &r);
			if (op == 1) {
				long long v;
				scanf("%lld", &v);
				if ((l + r) & 1)
					r--;
				if (l & 1)
					update(1, ceil(l, 2), ceil(r, 2), v, 1, no, 1);
				else 
					update(0, ceil(l, 2), ceil(r, 2), v, 1, ne, 1);
			}
			else {
				int Ol = ceil((l & 1 ? l : l + 1), 2);
				int Or = ceil((r & 1 ? r : r - 1), 2);
				int El = ceil((l & 1 ? l + 1 : l), 2);
				int Er = ceil((r & 1 ? r - 1 : r), 2);
				long long qo = 0, qe = 0;
				if (Ol <= Or)//区间合法
					qo = query(1, Ol, Or, 1, no, 1);
				if (flag && El <= Er)
					qe = query(0, El, Er, 1, ne, 1);
				printf("%lld\n", qo + qe);
			}
		}
	}
	return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

奇偶线段树(区间更新)

标签:区间更新

原文地址:http://blog.csdn.net/u014357885/article/details/47667221

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!