这里推荐一篇博客,原理讲的清晰易懂,配合着讲解更容易理解模板的思想
(http://t.csdn.cn/AaXFB)
void build(int p, int l, int r)//创建线段树,id表示存储下标,区间[L,r]
{
tr[p].l = l, tr[p].r = r, tr[p].lz = 0;
if (l == r)//左端点等于右端点,即为叶子节点(区间长度为1),直接赋值即可
{
tr[p].sum = a[l];
return;
}
// 否则将当前区间中间拆开成两个区间
int mid = (l + r) / 2;//mid则为中间点,左儿子的结点区间为[l,mid],右儿子的结点区间为[mid + 1,r]
build(p * 2, l, mid); //递归构造左儿子结点
build(p * 2 + 1, mid + 1, r); //递归构造右儿子结点
//tr[p].sum = min(tr[p * 2].sum, tr[p * 2 + 1].sum);//求区间最小值
tr[p].sum = tr[p * 2].sum + tr[2 * p + 1].sum;//求区间之和
return;
}
void add(int i, int dis, int k) {
if (tr[i].l == tr[i].r) {// 找到长度为 1 的区间才返回
tr[i].sum += k;
return;
}
if (dis <= tr[i * 2].r) add(i * 2, dis, k);
else add(i * 2 + 1, dis, k);
tr[i].sum = tr[i * 2].sum + tr[i * 2 + 1].sum;//更新区间和
return;
}
int find(int index, int l, int r) {//index是要查找的元素下标
if (l==r) {
return tr[l].sum;
}
int mid = (l + r) / 2;
if (index <= mid) {
return find(index, l, mid);
}
else {
return find(index, mid + 1, r);
}
}
void push_down(int p)
{
if (tr[p].lz)//如果id有lazy标记
{
tr[p * 2].lz += tr[p].lz;//将它的左孩子的lazy加上它的lazy
tr[p * 2 + 1].lz += tr[p].lz;//将它的右孩子的lazy加上它的lazy
int mid = (tr[p].l + tr[p].r) / 2;
tr[p * 2].sum += tr[p].lz * (mid - tr[2 * p].l + 1);//左孩子的Q+它下放的Q*区间长度
tr[p * 2 + 1].sum += tr[p].lz * (tr[2 * p + 1].r - mid);
tr[p].lz = 0;//清空lazy标记
}
}
void query(int p, int l, int r, int k)
{
if (tr[p].l >= l && tr[p].r <= r)//被[l,r]包含了
{
tr[p].lz += k;//暂时不下放,加进lazy标记中
tr[p].sum += (tr[p].r - tr[p].l + 1) * k;
return;
}
push_down(p);//要来更新下面节点了,赶紧下放
int mid = (tr[p].l + tr[p].r) / 2;
if (l <= mid) query(p * 2, l, r, k);//因为只有x<=mid(即[l,mid]有一部分是被[x,y]覆盖了的)才需要去更新[l,mid]
if (r > mid) query(p * 2 + 1, l, r, k);
tr[p].sum = tr[2 * p].sum + tr[2 * p + 1].sum;//子节点更新完之后父节点当然也要更新(上升操作)
}
void push_down(int p) {
// 该函数将父节点的懒惰更新(lz)传递给其子节点。
if (tr[p].lz) {
tr[2 * p].sum = tr[p].lz * (tr[2 * p].r - tr[2 * p].l + 1);
tr[2 * p + 1].sum = tr[p].lz * (tr[2 * p + 1].r - tr[2 * p + 1].l + 1);
tr[2 * p].lz = tr[p].lz;
tr[2 * p + 1].lz = tr[p].lz;
// 重置当前节点的懒惰更新值。
tr[p].lz = 0;
}
}
void update(int p, int l, int r, int k) {
// 如果当前节点的范围完全包含在查询范围内,执行懒惰更新并返回。
if (l <= tr[p].l && tr[p].r <= r) {
tr[p].sum = (tr[p].r - tr[p].l + 1) * k;
tr[p].lz = k;
return;
}
push_down(p);//下放
int m = (tr[p].l + tr[p].r) >> 1;
if (l <= m) update(2 * p, l, r, k);
if (r > m) update(2 * p + 1, l, r, k);
// 基于更新后的子节点和更新当前节点的和。
tr[p].sum = tr[2 * p].sum + tr[2 * p + 1].sum;
}
int find(int p, int l, int r)
{
if (tr[p].l >= l && tr[p].r <= r) return tr[p].sum;//[l,r]被[x,y]包含了
push_down(p);//要查到id的子节点了,赶紧下放
int ans = 0;
if (tr[2*p].r >= l) ans += find(p * 2, l, r);//ans+=左孩子和
if (tr[2*p+1].l <= r) ans += find(p * 2 + 1, l, r);//ans+=右孩子和
return ans;
}
#include
using namespace std;
#define int long long//防止溢出
const int N = 1e5 + 5;
int a[N];
struct node
{
int l, r, sum;
int lz;
}tr[N * 4];//4倍空间
void build(int p, int l, int r);
void push_down(int p);
void query(int p, int l, int r, int k);
int find(int, int , int );
signed main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
for (int i = 0; i < m; i++) {
int flag, l, r, k;
cin >> flag;
if (flag == 1) {
cin >> l >> r >> k;
query(1, l, r, k);
}
else {
cin >> l >> r;
cout << find(1, l, r)<= l && tr[p].r <= r)//被[l,r]包含了
{
tr[p].lz += k;//暂时不下放,加进lazy标记中
tr[p].sum += (tr[p].r - tr[p].l + 1) * k;
return;
}
push_down(p);//要来更新下面节点了,赶紧下放
int mid = (tr[p].l + tr[p].r) / 2;
if (l <= mid) query(p * 2, l, r, k);//因为只有x<=mid(即[l,mid]有一部分是被[x,y]覆盖了的)才需要去更新[l,mid]
if (r > mid) query(p * 2 + 1, l, r, k);
tr[p].sum = tr[2 * p].sum + tr[2 * p + 1].sum;//子节点更新完之后父节点当然也要更新(上升操作)
}
int find(int p, int l, int r)
{
if (tr[p].l >= l && tr[p].r <= r) return tr[p].sum;//[l,r]被[x,y]包含了
push_down(p);//要查到id的子节点了,赶紧下放
int ans = 0;
if (tr[2*p].r >= l) ans += find(p * 2, l, r);//ans+=左孩子和
if (tr[2*p+1].l <= r) ans += find(p * 2 + 1, l, r);//ans+=右孩子和
return ans;
}
#include
using namespace std;
#define int long long//防止溢出
const int N = 5e5 + 5;
int a[N];
struct node
{
int l, r, sum;
int lz;
}tr[N * 4];//4倍空间
void build(int p, int l, int r);
void add(int i, int dis, int k);
int find(int p, int l, int r);
signed main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
for (int i = 0; i < m; i++) {
int flag, l, r, pos, k;
cin >> flag;
if (flag == 1) {
cin >> pos >> k;
add(1, pos, k);
}
else {
cin >> l >> r;
cout << find(1, l, r)<= l && tr[p].r <= r) return tr[p].sum;//[l,r]被[x,y]包含了
//push_down(p);//要查到id的子节点了,赶紧下放
int ans = 0;
if (tr[2 * p].r >= l) ans += find(p * 2, l, r);//ans+=左孩子和
if (tr[2 * p + 1].l <= r) ans += find(p * 2 + 1, l, r);//ans+=右孩子和
return ans;
}
Tip:这题输入和输出最好用scanf()和printf(),不然可能会超时.
#include
using namespace std;
#define int long long//防止溢出
const int N = 5e5 + 5;
int a[N];
struct node
{
int l, r, sum;
int lz;
}tr[N * 4];//4倍空间
void build(int p, int l, int r);
void push_down(int p);
void query(int p, int l, int r, int k);
int find(int index, int l, int r);
signed main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
for (int i = 0; i < m; i++) {
int flag, l, r, pos, k;
cin >> flag;
if (flag == 1) {
cin >> l >> r >> k;
query(1, l, r, k);
}
else {
cin >> l;
cout << find(1, l, l)<= l && tr[p].r <= r)//被[l,r]包含了
{
tr[p].lz += k;//暂时不下放,加进lazy标记中
tr[p].sum += (tr[p].r - tr[p].l + 1) * k;
return;
}
push_down(p);//要来更新下面节点了,赶紧下放
int mid = (tr[p].l + tr[p].r) / 2;
if (l <= mid) query(p * 2, l, r, k);//因为只有x<=mid(即[l,mid]有一部分是被[x,y]覆盖了的)才需要去更新[l,mid]
if (r > mid) query(p * 2 + 1, l, r, k);
tr[p].sum = tr[2 * p].sum + tr[2 * p + 1].sum;//子节点更新完之后父节点当然也要更新(上升操作)
}
int find(int p, int l, int r)
{
if (tr[p].l >= l && tr[p].r <= r) return tr[p].sum;//[l,r]被[x,y]包含了
push_down(p);//要查到id的子节点了,赶紧下放
int ans = 0;
if (tr[2 * p].r >= l) ans += find(p * 2, l, r);//ans+=左孩子和
if (tr[2 * p + 1].l <= r) ans += find(p * 2 + 1, l, r);//ans+=右孩子和
return ans;
}
Tip:要写两个懒人标记分别表示加法和乘法,这里的输入和输出也是要用scanf()和printf().超时警告!!!
#include
using namespace std;
const int N = 1e5 + 5;
int a[N], mod;
struct node {
int l, r, sum; // 左右区间边界和当前区间元素和
int ad, mul; // 懒标记:加法和乘法
} tr[N * 4];
// 函数声明
void build(int p, int l, int r);
void push_down(int p);
void add(int p, int l, int r, int k);
void mult(int p, int l, int r, int k);
int search(int p, int l, int r);
signed main() {
int n, m;
cin >> n >> m >> mod; // 输入数列个数n,操作个数m,模数mod
for (int i = 1; i <= n; i++) cin >> a[i]; // 输入数列元素值
build(1, 1, n); // 构建线段树
for (int i = 0; i < m; i++) {
int flag, l, r, k;
cin >> flag; // 输入操作标识
if (flag == 1) {
cin >> l >> r >> k; // 输入区间 [l, r] 和乘法因子 k
mult(1, l, r, k); // 执行乘法操作
}
else if (flag == 2) {
cin >> l >> r >> k; // 输入区间 [l, r] 和加法值 k
add(1, l, r, k); // 执行加法操作
}
else {
cin >> l >> r; // 输入区间 [l, r]
cout << search(1, l, r) << endl; // 输出区间元素和对模数取模的结果
}
}
return 0;
}
void build(int p, int l, int r) {
tr[p].l = l, tr[p].r = r, tr[p].mul = 1;
if (l == r) {
tr[p].sum = a[l] % mod; // 叶节点直接记录数列元素对模数取模的值
return;
}
int mid = (l + r) / 2;
build(2 * p, l, mid); // 递归构建左子树
build(2 * p + 1, mid + 1, r); // 递归构建右子树
tr[p].sum = (tr[2 * p].sum + tr[2 * p + 1].sum) % mod; // 计算当前节点的元素和
}
void push_down(int p) {
// 将当前节点的懒标记向下传递给子节点
tr[2 * p].sum = (tr[2 * p].sum * tr[p].mul + tr[p].ad * (tr[2 * p].r - tr[2 * p].l + 1)) % mod;
tr[2 * p + 1].sum = (tr[2 * p + 1].sum * tr[p].mul + tr[p].ad * (tr[2 * p + 1].r - tr[2 * p + 1].l + 1)) % mod;
tr[2 * p].mul = (tr[2 * p].mul * tr[p].mul) % mod;
tr[2 * p + 1].mul = (tr[2 * p + 1].mul * tr[p].mul) % mod;
tr[2 * p].ad = (tr[2 * p].ad * tr[p].mul + tr[p].ad) % mod;
tr[2 * p + 1].ad = (tr[2 * p + 1].ad * tr[p].mul + tr[p].ad) % mod;
tr[p].ad = 0; // 清空当前节点的懒标记
tr[p].mul = 1;
return;
}
void add(int p, int l, int r, int k) {
if (tr[p].l >= l && tr[p].r <= r) {
tr[p].ad = (tr[p].ad + k) % mod;
tr[p].sum = (tr[p].sum + (tr[p].r - tr[p].l + 1) * k) % mod;
return;
}
push_down(p);
int mid = (tr[p].l + tr[p].r) / 2;
if (l <= mid) add(2 * p, l, r, k);
if (r > mid) add(2 * p + 1, l, r, k);
tr[p].sum = (tr[2 * p].sum + tr[2 * p + 1].sum) % mod;
return;
}
void mult(int p, int l, int r, int k) {
if (tr[p].l >= l && tr[p].r <= r) {
tr[p].ad = (tr[p].ad * k) % mod;
tr[p].mul = (tr[p].mul * k) % mod;
tr[p].sum = (tr[p].sum * k) % mod;
return;
}
push_down(p);
int mid = (tr[p].l + tr[p].r) / 2;
if (l <= mid) mult(2 * p, l, r, k);
if (r > mid) mult(2 * p + 1, l, r, k);
tr[p].sum = (tr[2 * p].sum + tr[2 * p + 1].sum) % mod;
}
int search(int p, int l, int r) {
if (tr[p].l >= l && tr[p].r <= r) return tr[p].sum; //[l,r]被[x,y]包含了
push_down(p); // 要查到id的子节点了,赶紧下放
int ans = 0;
if (tr[2 * p].r >= l) ans = (ans + search(p * 2, l, r)) % mod; // ans+=左孩子和
if (tr[2 * p + 1].l <= r) ans = (ans + search(p * 2 + 1, l, r)) % mod; // ans+=右孩子和
return ans;
}
Tip:同样的,这里的输入和输出也是要用scanf()和printf().
#include
using namespace std;
const int N = 1e5 + 5;
int a[N];
struct node
{
int l, r, sum;
int lz;
}tr[N*4];
void build(int p, int l, int r);
void push_down(int p);
void update(int p, int l, int r, int k);
int main()
{
int T, count = 0;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) a[i] = 1;
build(1, 1, n);
for (int i = 0; i < m; i++) {
int l, r, k;
cin >> l >> r >> k;
update(1, l, r, k);
}
count++;
cout << "Case " << count << ": The total value of the hook is " << tr[1].sum << "." << endl;
}
return 0;
}
void build(int p, int l, int r)//创建线段树,id表示存储下标,区间[L,r]
{
tr[p].l = l, tr[p].r = r, tr[p].lz = 0;
if (l == r)//左端点等于右端点,即为叶子节点(区间长度为1),直接赋值即可
{
tr[p].sum = a[l];
return;
}
// 否则将当前区间中间拆开成两个区间
int mid = (l + r) / 2;//mid则为中间点,左儿子的结点区间为[l,mid],右儿子的结点区间为[mid + 1,r]
build(p * 2, l, mid); //递归构造左儿子结点
build(p * 2 + 1, mid + 1, r); //递归构造右儿子结点
//tr[p].sum = min(tr[p * 2].sum, tr[p * 2 + 1].sum);//求区间最小值
tr[p].sum = tr[p * 2].sum + tr[2 * p + 1].sum;//求区间之和
return;
}
void push_down(int p) {
// 该函数将父节点的懒惰更新(lz)传递给其子节点。
if (tr[p].lz) {
tr[2 * p].sum = tr[p].lz * (tr[2 * p].r - tr[2 * p].l + 1);
tr[2 * p + 1].sum = tr[p].lz * (tr[2 * p + 1].r - tr[2 * p + 1].l + 1);
tr[2 * p].lz = tr[p].lz;
tr[2 * p + 1].lz = tr[p].lz;
// 重置当前节点的懒惰更新值。
tr[p].lz = 0;
}
}
void update(int p, int l, int r, int k) {
// 如果当前节点的范围完全包含在查询范围内,执行懒惰更新并返回。
if (l <= tr[p].l && tr[p].r <= r) {
tr[p].sum = (tr[p].r - tr[p].l + 1) * k;
tr[p].lz = k;
return;
}
push_down(p);//下放
int m = (tr[p].l + tr[p].r) >> 1;
if (l <= m) update(2 * p, l, r, k);
if (r > m) update(2 * p + 1, l, r, k);
// 基于更新后的子节点和更新当前节点的和。
tr[p].sum = tr[2 * p].sum + tr[2 * p + 1].sum;
}
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
const int N = 1e5 + 5;
int a[N];
struct node
{
int l, r, sum;
int lz;
}tr[N * 4];//4倍空间
void build(int p, int l, int r);
void update(int p, int pos, int k);
int query(int p, int l, int r);
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);//提高输入输出效率
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
for (int i = 0; i < m; i++) {
char flag;
int l, r;
cin >> flag >> l >> r;
if (flag == 'U') update(1, l, r);
else cout << query(1, l, r) << endl;
}
return 0;
}
void build(int p, int l, int r)//创建线段树,id表示存储下标,区间[L,r]
{
tr[p].l = l, tr[p].r = r, tr[p].lz = 0;
if (l == r)//左端点等于右端点,即为叶子节点(区间长度为1),直接赋值即可
{
tr[p].sum = a[l];
return;
}
// 否则将当前区间中间拆开成两个区间
int mid = (l + r) / 2;//mid则为中间点,左儿子的结点区间为[l,mid],右儿子的结点区间为[mid + 1,r]
build(p * 2, l, mid); //递归构造左儿子结点
build(p * 2 + 1, mid + 1, r); //递归构造右儿子结点
tr[p].sum = max(tr[p * 2].sum, tr[p * 2 + 1].sum);
return;
}
void update(int i, int dis, int k) {
if (tr[i].l == tr[i].r) {// 找到长度为 1 的区间才返回
tr[i].sum = k;
return;
}
if (dis <= tr[i * 2].r) update(i * 2, dis, k);
else update(i * 2 + 1, dis, k);
tr[i].sum = max(tr[i * 2].sum, tr[i * 2 + 1].sum);//更新区间和
return;
}
int query(int p, int l, int r)
{
if (tr[p].l >= l && tr[p].r <= r) return tr[p].sum;//[l,r]被[x,y]包含了
//push_down(p);//要查到id的子节点了,赶紧下放
int ans = 0;
if (tr[2 * p].r >= l) ans = max(ans, query(p * 2, l, r));//ans+=左孩子和
if (tr[2 * p + 1].l <= r) ans = max(ans, query(p * 2 + 1, l, r));//ans+=右孩子和
return ans;
}