题目链接: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;
}