int getsum(int l, int r, int s, int t, int p) {
//取得区间和,l、r是最终目标范围,s、t是当前递归范围,p是当前范围的节点编号
if (l <= s && t <= r) {
//如果当前区间全部在目标范围内,直接返回
return d[p];
}
int m = s + (t - s) / 2; //拆分,递归
if (b[p]) { //如果有标记,往子节点访问要更新
d[p * 2] += b[p] * (m - s + 1);
d[p * 2 + 1] += b[p] * (t - m);
b[p * 2] += b[p];
b[p * 2 + 1] += b[p];
b[p] = 0;
}
int sum = 0;
if (l <= m) {
//左半边有目标范围
sum += getsum(l, r, s, m, p * 2);
}
if (r > m) {
//右半边(注意右半边不包括m)有目标范围
sum += getsum(l, r, m + 1, t, p * 2 + 1);
}
return sum;
}
add函数
void add(int l, int r, int c, int s, int t, int p) {
//区间加(减),l,r是最终目标区间,c是增加的值,可以为负。s,t是当前区间,p是当前区间节点编号
if (l <= s && t <= r) { //目标全包含当前区间,计算后返回
d[p] += (t - s + 1) * c;
b[p] += c; //标记好
return;
}
//具体要往子节点访问,更新子节点并消除标记
int m = s + ((t - s) / 2);
if (b[p] && s != t) { //非叶子带标记,更新子节点值并下沉标记
d[p * 2] += b[p] * (m - s + 1);//总和是修改了b[p]*num的
d[p * 2 + 1] += b[p] * (t - m);
b[p * 2] += b[p]; //标记下沉
b[p * 2 + 1] += b[p];
b[p] = 0;
}
if (l <= m) add(l, r, c, s, m, p * 2);
if (r > m) add(l, r, c, m + 1, t, p * 2 + 1);
d[p] = d[p * 2] + d[p * 2 + 1];
}
调用示例
注意,建树时节点编号必须>0,否则n*0都是0,整个树都会发生错误。
int main() {
for (int i = 1; i <= 10; i++) { a[i] = 1;}
build(1, 10, 1);
add(1, 4, 0, 1, 10, 1);
printf("%d", getsum(1, 10, 1, 10, 1));
}
提供区间加减法、区间求和的完整代码模板:
#include<cstdio>
using namespace std;
#define maxn 10100
int d[maxn * 4];//存树
int b[maxn * 4];//lazytag标记
int a[maxn];//原数据集
void build(int s, int t, int p) {
//建立线段树,对[s,t]建树,当前根节点编号为p
if (s == t) { //找到单个数了
d[p] = a[s];
return;
}
int m = s + ((t - s) / 2); //求中间值,二分递归
build(s, m, p * 2);//p*2是左子节点
build(m + 1, t, p * 2+1);// 右子节点
d[p] = d[p * 2] + d[p * 2 + 1];//更新节点
}
int getsum(int l, int r, int s, int t, int p) {
//取得区间和,l、r是最终目标范围,s、t是当前递归范围,p是当前范围的节点编号
if (l <= s && t <= r) {
//如果当前区间全部在目标范围内,直接返回
return d[p];
}
int m = s + (t - s) / 2; //拆分,递归
if (b[p]) { //如果有标记,往子节点访问要更新
d[p * 2] += b[p] * (m - s + 1);
d[p * 2 + 1] += b[p] * (t - m);
b[p * 2] += b[p];
b[p * 2 + 1] += b[p];
b[p] = 0;
}
int sum = 0;
if (l <= m) {
//左半边有目标范围
sum += getsum(l, r, s, m, p * 2);
}
if (r > m) {
//右半边(注意右半边不包括m)有目标范围
sum += getsum(l, r, m + 1, t, p * 2 + 1);
}
return sum;
}
void add(int l, int r, int c, int s, int t, int p) {
//区间加(减),l,r是最终目标区间,c是增加的值,可以为负。s,t是当前区间,p是当前区间节点编号
if (l <= s && t <= r) { //目标全包含当前区间,计算后返回
d[p] += (t - s + 1) * c;
b[p] += c; //标记好
return;
}
//具体要往子节点访问,更新子节点并消除标记
int m = s + ((t - s) / 2);
if (b[p] && s != t) { //非叶子带标记,更新子节点值并下沉标记
d[p * 2] += b[p] * (m - s + 1);//总和是修改了b[p]*num的
d[p * 2 + 1] += b[p] * (t - m);
b[p * 2] += b[p]; //标记下沉
b[p * 2 + 1] += b[p];
b[p] = 0;
}
if (l <= m) add(l, r, c, s, m, p * 2);
if (r > m) add(l, r, c, m + 1, t, p * 2 + 1);
d[p] = d[p * 2] + d[p * 2 + 1];
}
int main() {
for (int i = 1; i <= 10; i++) { a[i] = 1; b[i] = 0; }
build(1, 10, 1);
add(1, 4, 0, 1, 10, 1);
printf("%d", getsum(1, 10, 1, 10, 1));
}
提供区间加减、乘法、区间求和的代码模板:
#include<cstdio>
using namespace std;
#define maxn 10100
int d[maxn * 4];//存树
int b[maxn * 4];//lazytag标记
int b2[maxn * 4];//标记2
int a[maxn];//原数据集
void build(int s, int t, int p) {
//建立线段树,对[s,t]建树,当前根节点编号为p
if (s == t) { //找到单个数了
d[p] = a[s];
return;
}
int m = s + ((t - s) / 2); //求中间值,二分递归
build(s, m, p * 2);//p*2是左子节点
build(m + 1, t, p * 2+1);// 右子节点
d[p] = d[p * 2] + d[p * 2 + 1];//更新节点
d[p] %= 998244353;
}
int getsum(int l, int r, int s, int t, int p) {
//取得区间和,l、r是最终目标范围,s、t是当前递归范围,p是当前范围的节点编号
if (l <= s && t <= r) {
//如果当前区间全部在目标范围内,直接返回
return d[p];
}
int m = s + (t - s) / 2; //拆分,递归
if (b[p]) { //如果有标记,往子节点访问要更新
d[p * 2] += b[p] * (m - s + 1);
d[p*2] %= 998244353;
d[p * 2 + 1] += b[p] * (t - m);
d[p * 2+1] %= 998244353;
b[p * 2] += b[p];
b[p * 2 + 1] += b[p];
b[p] = 1;
}
if (b2[p]>1) { //如果有标记2,往子节点访问要更新
d[p * 2] *= b2[p];
d[p * 2] %= 998244353;
d[p * 2 + 1] *= b2[p];
d[p * 2 + 1] %= 998244353;
b2[p * 2] *= b2[p];
b2[p * 2 + 1] *= b2[p];
b2[p] = 0;
}
int sum = 0;
if (l <= m) {
//左半边有目标范围
sum += getsum(l, r, s, m, p * 2);
sum %=998244353;
}
if (r > m) {
//右半边(注意右半边不包括m)有目标范围
sum += getsum(l, r, m + 1, t, p * 2 + 1);
sum %= 998244353;
}
return sum;
}
void add(int l, int r, int c, int s, int t, int p) {
//区间加(减),l,r是最终目标区间,c是增加的值,可以为负。s,t是当前区间,p是当前区间节点编号
if (l <= s && t <= r) { //目标全包含当前区间,计算后返回
d[p] += (t - s + 1) * c;
d[p]%= 998244353;
b[p] += c; //标记好
return;
}
//具体要往子节点访问,更新子节点并消除标记
int m = s + ((t - s) / 2);
if (b[p] && s != t) { //非叶子带标记,更新子节点值并下沉标记
d[p * 2] += b[p] * (m - s + 1);//总和是修改了b[p]*num的
d[p * 2 + 1] += b[p] * (t - m);
d[p * 2] %= 998244353;
d[p * 2 + 1] %= 998244353;
b[p * 2] += b[p]; //标记下沉
b[p * 2 + 1] += b[p];
b[p] = 0;
}
if (l <= m) add(l, r, c, s, m, p * 2);
if (r > m) add(l, r, c, m + 1, t, p * 2 + 1);
d[p] = (d[p * 2] + d[p * 2 + 1]) % 998244353;
}
void mul(int l, int r, int c, int s, int t, int p) {
//区间乘法,l,r是最终目标区间,c是乘的值,可以为负。s,t是当前区间,p是当前区间节点编号
if (l <= s && t <= r) { //目标全包含当前区间,计算后返回
d[p] *= c;
b2[p] *= c; //标记好
d[p] %= 998244353;
return;
}
//具体要往子节点访问,更新子节点并消除标记
int m = s + ((t - s) / 2);
if (b2[p]>1 && s != t) { //非叶子带标记,更新子节点值并下沉标记
d[p * 2] *= b2[p];//这里是乘积,直接乘就行了
d[p * 2 + 1] *= b2[p];
d[p * 2] %= 998244353;
d[p * 2 + 1] %= 998244353;
b2[p * 2] *= b2[p]; //标记下沉
b2[p * 2 + 1] *= b2[p];
b2[p] = 1;
}
if (l <= m) mul(l, r, c, s, m, p * 2);
if (r > m) mul(l, r, c, m + 1, t, p * 2 + 1);
d[p] = (d[p * 2] + d[p * 2 + 1]) % 998244353;
}
int main() {
for (int i = 1; i <= 10; i++) { a[i] = 1; b[i] = 0; b2[i] = 1; }
build(1, 10, 1);
mul(2, 6, 2, 1, 10, 1);
printf("%d", getsum(1, 10, 1, 10, 1));
}