Limit线段树题单题解(更新中)
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线段树题单题解(更新中)的更多相关文章
- Codeforces295A - Greg and Array(线段树的成段更新)
题目大意 给定一个序列a[1],a[2]--a[n] 接下来给出m种操作,每种操作是以下形式的: l r d 表示把区间[l,r]内的每一个数都加上一个值d 之后有k个操作,每个操作是以下形式的: x ...
- HDU 1754 I Hate It(线段树区间查询,单点更新)
描述 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感.不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老 ...
- 知道创宇爬虫题--代码持续更新中 - littlethunder的专栏 - 博客频道 - CSDN.NET
知道创宇爬虫题--代码持续更新中 - littlethunder的专栏 - 博客频道 - CSDN.NET undefined 公司介绍 - 数人科技 undefined
- POJ3648 A Simple Problem with Integers(线段树之成段更新。入门题)
A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 53169 Acc ...
- hdu1166 敌兵布阵(线段树 求区间和 更新点)
敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- POJ2828 Buy Tickets 【线段树】+【单点更新】+【逆序】
Buy Tickets Time Limit: 4000MS Memory Limit: 65536K Total Submissions: 12296 Accepted: 6071 Desc ...
- hdu 1754 I Hate It (线段树功能:单点更新和区间最值)
版权声明:本文为博主原创文章.未经博主同意不得转载.vasttian https://blog.csdn.net/u012860063/article/details/32982923 转载请注明出处 ...
- HDU 2795 线段树区间最大值,单点更新+二分
Billboard Time Limit: 20000/8000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 线段树之成段更新( 需要用到延迟标记,简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候)
HDU 1698 链接: http://acm.hdu.edu.cn/showproblem.php?pid=1698 线段树功能:update:成段替换 (由于只query一次总区间,所以可以直 ...
- XOR on segment(线段树区间异或更新)
原题传送门 本题大意:给定n个数字和m个操作,操作共有两种,第一种是求解区间l到r上元素的和,第二种是将区间l到r的元素都异或一个x,作为某个位置的新值. 很容易想到线段树维护区间和,但是我们发现,在 ...
随机推荐
- 有哪些让你「 爽到爆炸 」的 Windows 软件?
前言 本文源于知乎的一个提问,如标题所示:有哪些让你「 爽到爆炸 」的 Windows 软件?今天大姚给大家分享6款C#/.NET开源且免费的Windows软件,希望可以帮助大家提高学习.开发.办公效 ...
- 公有云-实验一 实践腾讯云部署Web应用
实验一 实践腾讯云部署Web应用 概述 企业A需要搭建一套在互联网上发布的论坛平台,但是企业内部并没有完善的基础架构设施,难以保证论坛平台的高可用性和高安全性.经过IT部门相关专家分析讨论,决定在腾讯 ...
- fluent python-chap3-1
class collections.OrderedDict([items]) 返回一个 dict 子类的实例,它具有专门用于重新排列字典顺序的方法. """ move_t ...
- java_day2_常量,变量,数据类型,运算符
一.常量 常量:在Java程序运行过程中其值不能发生改变的量 分类: 1.字面值常量: 整数常量 表示所有的整数,包括负数 10 -8 小数常量 表示所有的小数 1.23 -3.14 布尔常量 tru ...
- MyBatisPlus——简介
概述 MyBatisPlus(简称MP)是基于MyBatisPlus框架基础上开发的增强型工具,旨在简化开发.提高效率 国内开发的技术 特性 无侵入:只做增强不做改变,不会对现有工程产生影响 强大的C ...
- 线段树can you answer these queries-------hdu4027
问题描述: 给定一个数列,要求对指定区间内所有数开方,输出查询区间和 输入: 有很多个测试用例,每个用例第一行输出一个整数N,表示数列有N个数,1<=N<=100000;第二行输入N个整数 ...
- Centos LNMP 安装日记
环境介绍 [root@k8s-master ~]# cat /etc/redhat-release CentOS Linux release 7.7.1908 (Core) mysql8.0.12_b ...
- `operator++(int)` 和 `operator++()`
operator++(int) 和 operator++() 是 C++ 中重载的两个不同的自增运算符函数,它们分别用于后置自增和前置自增.它们的区别在于调用方式以及自增行为的不同. 1. 前置自增运 ...
- Oracle、MySQL等数据库故障处理优质文章分享 | 10月汇总
墨天轮社区于9月起持续举办[聊聊故障处理那些事儿]DBA专题征文活动,每月进行评优发奖,鼓励大家记录工作中遇到的数据库故障处理过程,不仅用于自我复盘与分析,同时也能帮助其他的同仁们避坑. 上月为大家梳 ...
- apisix~helm方式的部署到k8s
什么是apisix Apache APISIX 是一个高性能.轻量级的开源 API 网关,基于 Nginx 和 OpenResty 构建.APISIX 提供了丰富的功能和灵活的配置,适用于构建现代的微 ...



