P3373 线段树模板 2

\(1 \leq n \leq 10^5\)

题解:考查标记与标记的合并

  • 我们考虑打两个懒惰标记实现区间乘和区间加
  • 线段树维护区间和
  • 对于信息与信息的合并:左儿子加上右儿子即可
  • 对于标记与标记的合并:
  • 我们发现如果某个区间存在加法的\(lazy\),但是现在对该区间又打上乘法的\(lazy\),如果我们不更新加法的\(lazy\),那么下放加法\(lazy\)的时候会出现错误
  • 所以我们在打乘法\(lazy\)的时候也要将加法的\(lazy\)乘上相应的标记值,这是两个不同的标记之间的合并
  • 对于相同标记之间,直接合并即可
  • 对于信息和标记的合并,简单合并即可
  • 标记下放时我们注意,因为加法的\(lazy\)有可能被更新过了,所以我们应该先下放乘法标记并更新区间信息,再下放加法标记并更新区间信息即可
const int N = 1e5 + 10, M = 4e5 + 10;

int n, q, m;
int a[N];
struct info
{
int sum;
};
struct SEG
{
int mul_lazy, add_lazy;
int len;
info val;
SEG()
{
mul_lazy = 1;
add_lazy = 0;
}
} seg[N << 2];
info operator+(const info &a, const info &b)
{
info c;
c.sum = (a.sum + b.sum) % m;
return c;
} void settag_mul(int id, int tag)
{
seg[id].val.sum = seg[id].val.sum * tag % m;
seg[id].mul_lazy = seg[id].mul_lazy * tag % m;
seg[id].add_lazy = seg[id].add_lazy * tag % m;
} void settag_add(int id, int tag)
{
seg[id].val.sum = (seg[id].val.sum + seg[id].len * tag % m) % m;
seg[id].add_lazy = (seg[id].add_lazy + tag) % m;
} void up(int id)
{
seg[id].val = seg[lson].val + seg[rson].val;
} void down(int id)
{
if (seg[id].mul_lazy != 1)
{
settag_mul(lson, seg[id].mul_lazy);
settag_mul(rson, seg[id].mul_lazy);
}
if (seg[id].add_lazy != 0)
{
settag_add(lson, seg[id].add_lazy);
settag_add(rson, seg[id].add_lazy);
}
seg[id].add_lazy = 0, seg[id].mul_lazy = 1;
} void build(int id, int l, int r)
{
seg[id].len = r - l + 1;
if (l == r)
{
seg[id].val.sum = a[l];
return;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
up(id);
} void modify(int id, int l, int r, int ql, int qr, int op, int val)
{
if (ql <= l && r <= qr)
{
if (op == 1)
settag_mul(id, val);
else
settag_add(id, val);
return;
}
down(id);
int mid = l + r >> 1;
if (qr <= mid)
modify(lson, l, mid, ql, qr, op, val);
else if (ql > mid)
modify(rson, mid + 1, r, ql, qr, op, val);
else
{
modify(lson, l, mid, ql, qr, op, val);
modify(rson, mid + 1, r, ql, qr, op, val);
}
up(id);
} info query(int id, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr)
{
return seg[id].val;
}
down(id);
int mid = l + r >> 1;
if (qr <= mid)
return query(lson, l, mid, ql, qr);
else if (ql > mid)
return query(rson, mid + 1, r, ql, qr);
else
return query(lson, l, mid, ql, qr) + query(rson, mid + 1, r, ql, qr);
} void solve()
{
cin >> n >> q >> m;
for (int i = 1; i <= n; ++i)
cin >> a[i];
build(1, 1, n);
while (q--)
{
int op, l, r, x;
cin >> op >> l >> r;
if (op == 1)
{
cin >> x;
modify(1, 1, n, l, r, 1, x);
}
else if (op == 2)
{
cin >> x;
modify(1, 1, n, l, r, 2, x);
}
else
{
cout << query(1, 1, n, l, r).sum % m << endl;
}
}
}

P1276 校门外的树(增强版)

\(1 \leq L \leq 10^4\)

题解:两颗线段树

  • 由于懒惰标记较为麻烦,我们考虑建两颗线段树
  • 一颗维护树和树苗存在的数量,另一颗维护树存在的数量
  • 那么对于答案我们可以通过简单容斥获得
  • 最后留下的树苗 = 最后留下的树和树苗的数量 - 最后留下的树的数量
  • 每次被砍掉的树苗 = 每次砍掉的树和树苗的数量 - 每次砍掉的树的数量,统计每次数量即可
  • 我们考虑线段树如何维护数量
  • 线段树维护区间和
  • 一开始所有叶子节点全为\(1\)
  • 每次砍树,我们将对应的区间的区间和赋值为\(0\),并利用懒惰标记更新下面的区间
  • 每次种树,我们将对应的区间的区间和赋值为区间长度,并利用懒惰标记更新下面的区间
const int N = 1e4 + 10, M = 4e5 + 10;

int n, q;
struct SEG_TREE
{
struct info
{
int sum;
// 信息与信息的合并
friend info operator+(const info &a, const info &b)
{
info c;
c.sum = a.sum + b.sum;
return c;
}
};
struct SEG
{
int lazy, len;
info val;
} seg[N << 2];
void settag(int id, int tag)
{
// 信息与标记的合并
if (tag == -1) // 砍树,区间赋0
seg[id].val.sum = 0;
else if (tag == 1) // 种树, 区间赋1
seg[id].val.sum = seg[id].len;
// 标记与标记的合并
seg[id].lazy = tag;
}
void up(int id)
{
seg[id].val = seg[lson].val + seg[rson].val;
}
void down(int id)
{
if (seg[id].lazy == 0)
return;
// 下放标记
settag(lson, seg[id].lazy);
settag(rson, seg[id].lazy);
// 清空标记
seg[id].lazy = 0;
}
void build(int id, int l, int r)
{
seg[id].len = r - l + 1;
if (l == r)
{
seg[id].val.sum = 1;
return;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
up(id);
}
void modify(int id, int l, int r, int ql, int qr, int val)
{
if (ql <= l && r <= qr)
{
settag(id, val);
return;
}
down(id);
int mid = l + r >> 1;
if (qr <= mid)
modify(lson, l, mid, ql, qr, val);
else if (ql > mid)
modify(rson, mid + 1, r, ql, qr, val);
else
{
modify(lson, l, mid, ql, qr, val);
modify(rson, mid + 1, r, ql, qr, val);
}
up(id);
}
info query(int id, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr)
{
return seg[id].val;
}
down(id);
int mid = l + r >> 1;
if (qr <= mid)
return query(lson, l, mid, ql, qr);
else if (ql > mid)
return query(rson, mid + 1, r, ql, qr);
else
return query(lson, l, mid, ql, qr) + query(rson, mid + 1, r, ql, qr);
}
} tr1, tr2; void solve()
{
cin >> n >> q;
++n;
tr1.build(1, 1, n), tr2.build(1, 1, n);
int ans = 0;
while (q--)
{
int op, l, r;
cin >> op >> l >> r;
++l, ++r;
if (op == 0)
{
// 被砍掉的树苗和树的总数, 被砍掉的树的总数
int sum1 = tr1.query(1, 1, n, 1, n).sum, sum2 = tr2.query(1, 1, n, 1, n).sum;
tr1.modify(1, 1, n, l, r, -1);
tr2.modify(1, 1, n, l, r, -1);
sum1 -= tr1.query(1, 1, n, 1, n).sum;
sum2 -= tr2.query(1, 1, n, 1, n).sum;
ans += sum1 - sum2;
}
else
{
tr1.modify(1, 1, n, l, r, 1);
}
}
cout << tr1.query(1, 1, n, 1, n).sum - tr2.query(1, 1, n, 1, n).sum << endl;
cout << ans << endl;
}

P5057 [CQOI2006] 简单题

\(1 \leq n \leq 10^5\)

题解:只有标记的线段树

  • 线段树不需要维护任何信息,只需要利用标记来完成数字反转
  • 对于\(lazy\)来说,\(1\)代表该区间需要反转,\(0\)代表不需要反转
  • 因为只有标记,所以我们只需要关注标记与标记的合并:
  • 如果某个区间上存在标记,说明该区间需要反转,如果我们再对该区间打上标记的话,该区间不反转,所以标记的合并实际上就是异或
  • 对于单点查询来说,如果该区间的\(lazy = 1\),直接输出\(1\)即可,否则输出\(0\)
const int N = 1e5 + 10, M = 4e5 + 10;

int n, q;
struct SEG
{
int lazy;
} seg[N << 2]; void settag(int id, int tag)
{
seg[id].lazy ^= tag;
} void down(int id)
{
if (seg[id].lazy == 0)
return;
int tag = seg[id].lazy;
settag(lson, tag);
settag(rson, tag);
seg[id].lazy = 0;
} void modify(int id, int l, int r, int ql, int qr, int tag)
{
if (ql <= l && r <= qr)
{
settag(id, tag);
return;
}
down(id);
int mid = l + r >> 1;
if (qr <= mid)
modify(lson, l, mid, ql, qr, tag);
else if (ql > mid)
modify(rson, mid + 1, r, ql, qr, tag);
else
{
modify(lson, l, mid, ql, qr, tag);
modify(rson, mid + 1, r, ql, qr, tag);
}
} int query(int id, int l, int r, int x)
{
if (l == r)
return seg[id].lazy;
down(id);
int mid = l + r >> 1;
if (x <= mid)
return query(lson, l, mid, x);
else
return query(rson, mid + 1, r, x);
} void solve()
{
cin >> n >> q;
while (q--)
{
int op, l, r, x;
cin >> op;
if (op == 1)
{
cin >> l >> r;
modify(1, 1, n, l, r, 1);
}
else
{
cin >> x;
if (query(1, 1, n, x) == 1)
cout << 1 << endl;
else
cout << 0 << endl;
}
}
}

P4588 [TJOI2018] 数学计算

\(1 \leq Q \leq 10^5\)

题解:对时间点建线段树

  • 我们考虑对于时间点建线段树
  • 线段树维护区间乘,初始所有区间为\(1\)
  • 对于操作一,我们直接在当前的时间点上修改即可
  • 对于操作二,我们直接在线段树将第\(pos\)次时间点上的修改为\(1\)即可
  • 对于查询来说,我们直接输出\(seg[1]\)即可
const int N = 1e5 + 10, M = 4e5 + 10;

int q, m;
int mp[N]; struct info
{
int ans;
};
struct SEG
{
info val;
} seg[N << 2];
info operator+(const info &a, const info &b)
{
info c;
c.ans = a.ans * b.ans % m;
return c;
} void up(int id)
{
seg[id].val = seg[lson].val + seg[rson].val;
} void build(int id, int l, int r)
{
if (l == r)
{
seg[id].val.ans = 1;
return;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
up(id);
} void change(int id, int l, int r, int x, int val)
{
if (l == r)
{
seg[id].val.ans = val;
return;
}
int mid = l + r >> 1;
if (x <= mid)
change(lson, l, mid, x, val);
else
change(rson, mid + 1, r, x, val);
up(id);
} void solve()
{
cin >> q >> m;
build(1, 1, q);
for (int i = 1; i <= q; ++i)
{
int op, x;
cin >> op >> x;
if (op == 1)
{
change(1, 1, q, i, x);
cout << seg[1].val.ans % m << endl;
}
else
{
change(1, 1, q, x, 1);
cout << seg[1].val.ans % m << endl;
}
}
}

P1908 逆序对

\(1 \leq n \leq 5\times10^5\)

题解:动态开点

const int N = 5e5 + 10, M = 8e6 + 10;

int n, m;
int a[N];
int rt, lson[M], rson[M], sum[M], idx; void up(int id)
{
sum[id] = sum[lson[id]] + sum[rson[id]];
} void change(int &id, int l, int r, int x, int val)
{
if (!id)
id = ++idx;
if (l == r)
{
sum[id] += val;
return;
}
int mid = l + r >> 1;
if (x <= mid)
change(lson[id], l, mid, x, val);
else
change(rson[id], mid + 1, r, x, val);
up(id);
} int query(int id, int l, int r, int ql, int qr)
{
if (!id)
return 0;
if (ql <= l && r <= qr)
{
return sum[id];
}
int mid = l + r >> 1;
if (qr <= mid)
return query(lson[id], l, mid, ql, qr);
else if (ql > mid)
return query(rson[id], mid + 1, r, ql, qr);
else
return query(lson[id], l, mid, ql, qr) + query(rson[id], mid + 1, r, ql, qr);
} void solve()
{
m = 1e9 + 10;
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
long long ans = 0;
for (int i = 1; i <= n; ++i)
{
ans += query(rt, 1, m, a[i] + 1, m);
change(rt, 1, m, a[i], 1);
}
cout << ans << endl;
}

Limit线段树题单题解(更新中)的更多相关文章

  1. Codeforces295A - Greg and Array(线段树的成段更新)

    题目大意 给定一个序列a[1],a[2]--a[n] 接下来给出m种操作,每种操作是以下形式的: l r d 表示把区间[l,r]内的每一个数都加上一个值d 之后有k个操作,每个操作是以下形式的: x ...

  2. HDU 1754 I Hate It(线段树区间查询,单点更新)

    描述 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感.不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老 ...

  3. 知道创宇爬虫题--代码持续更新中 - littlethunder的专栏 - 博客频道 - CSDN.NET

    知道创宇爬虫题--代码持续更新中 - littlethunder的专栏 - 博客频道 - CSDN.NET undefined 公司介绍 - 数人科技 undefined

  4. POJ3648 A Simple Problem with Integers(线段树之成段更新。入门题)

    A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 53169 Acc ...

  5. hdu1166 敌兵布阵(线段树 求区间和 更新点)

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  6. POJ2828 Buy Tickets 【线段树】+【单点更新】+【逆序】

    Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 12296   Accepted: 6071 Desc ...

  7. hdu 1754 I Hate It (线段树功能:单点更新和区间最值)

    版权声明:本文为博主原创文章.未经博主同意不得转载.vasttian https://blog.csdn.net/u012860063/article/details/32982923 转载请注明出处 ...

  8. HDU 2795 线段树区间最大值,单点更新+二分

    Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. 线段树之成段更新( 需要用到延迟标记,简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候)

    HDU  1698 链接:  http://acm.hdu.edu.cn/showproblem.php?pid=1698 线段树功能:update:成段替换 (由于只query一次总区间,所以可以直 ...

  10. XOR on segment(线段树区间异或更新)

    原题传送门 本题大意:给定n个数字和m个操作,操作共有两种,第一种是求解区间l到r上元素的和,第二种是将区间l到r的元素都异或一个x,作为某个位置的新值. 很容易想到线段树维护区间和,但是我们发现,在 ...

随机推荐

  1. 如何在 Nuxt 3 中有效使用 TypeScript

    title: 如何在 Nuxt 3 中有效使用 TypeScript date: 2024/9/9 updated: 2024/9/9 author: cmdragon excerpt: 摘要:本文详 ...

  2. 全网最适合入门的面向对象编程教程:46 Python函数方法与接口-函数与事件驱动框架

    全网最适合入门的面向对象编程教程:46 Python 函数方法与接口-函数与事件驱动框架 摘要: 函数是 Python 中的一等公民,是一种可重用的代码块,用于封装特定的逻辑:事件驱动框架是一种编程模 ...

  3. manim边学边做--通用多边形

    manim提供了通用多边形模块,可以绘制任意的多边形. 通用多边形模块有两种,Polygon和Polygram. Polygon是一个几何学术语,主要指的是由三条或三条以上的线段首尾顺次连接所组成的平 ...

  4. k8s 中的 Service 简介【k8s 系列之二】

    〇.前言 k8s 集群中的每一个 Pod 都有自己的 IP 地址,那么是不是有 IP 了,访问起来就简单了呢,其实不然. 因为在 k8s 中 Pod 不是持久性的,摧毁重建将获得新的 IP,客户端通过 ...

  5. Azure – WAF (Web Application Firewall)

    前言 最近有客户想购买 Azure 的 Web Application Firewall (WAF), 来防 SQL Injection, XSS 攻击. 一开始我是觉得没什么必要, 毕竟什么年代了, ...

  6. 二叉树的 Morris 中序遍历——O(1)空间复杂度

    回顾 问题陈述: 给定一棵二叉树,实现中序遍历并返回包含其中序序列的数组 例如给定下列二叉树: 我们按照左.根.右的顺序递归遍历二叉树,得到以下遍历: 最终中序遍历结果可以输出为: [3, 1, 9, ...

  7. Navicat Premium 12的下载和安装

    首先下载navicate程序以及破解文件,这里一并存到了百度网盘直接下载即可 链接:https://pan.baidu.com/s/1a2QKHHUZ0ej1D78CjQ1ldA提取码:klus 解压 ...

  8. 【赵渝强老师】MongoDB管理用户的认证机制

    一.MongoDB用户认证机制简介 为了认证客户端,你必须要添加一个对应的用户到MongoDB.基本的步骤分为以下几步: 用户管理接口:db.createUser()方法可以创建一个用户,添加完成后可 ...

  9. 《Vue.js 设计与实现》读书笔记 - 第15章、编译器核心技术概览

    第15章.编译器核心技术概览 15.1 模板 DSL 的编译器 完整的编译包括 [源代码] -->词法分析-->语法分析-->语义分析(编译前端) -->中间代码生成--> ...

  10. [rCore学习笔记 029] 动态内存分配器实现-以buddy_system_allocator源码为例

    在上一部分,我们讲了动态内存分配器的原理是维护一个堆,而且是实现各种连续内存分配方法. 但是上一部分是直接通过引用了buddy_system_allocator来解决的问题. 那么对于内存分配算法有兴 ...