今天来学习dls的讲的中级包里的数据结构课程里的线段树,主要内容有:
a
.
单点修改
,
区间查询
b
.
区间修改,区间查询
c
.
线段树上二分
a.单点修改,区间查询\\ b.区间修改,区间查询\\ c.线段树上二分
a.单点修改,区间查询b.区间修改,区间查询c.线段树上二分
第一题:线段树1
做法:单点修改和维护区间信息,这里有两个信息,一个是区间最小值,一个是区间最小值出现的次数,还算基础,递归一下就行了,代码如下:
/*
coder:sunshine
school:njupt
*/
#include <bits/stdc++.h>
using namespace std;
#define endl '\n' //交互题删掉
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int N = 2e5 + 100;
int n, q;
int a[N];
struct info
{
int minv, mincnt;
};
info operator+(const info &l, const info &r)
{
info a;
a.minv = min(l.minv, r.minv);
if (l.minv == r.minv)
a.mincnt = l.mincnt + r.mincnt;
else if (l.minv < r.minv)
a.mincnt = l.mincnt;
else
a.mincnt = r.mincnt;
return a;
}
struct node
{
info val;
/* data */
} seg[N * 4];
void update(int id)
{
seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}
void build(int id, int l, int r)
{
if (l == r)
{
seg[id].val = {a[l], 1};
}
else
{
int mid = (l + r) / 2;
build(id * 2, l, mid);
build(id * 2 + 1, mid + 1, r);
update(id);
}
}
// 节点为id,对应的区间为[l, r],修改a[pos] -> val
void change(int id, int l, int r, int pos, int val)
{
if (l == r)
{
seg[id].val = {val, 1};
}
else
{
int mid = (l + r) / 2;
if (pos <= mid)
change(id * 2, l, mid, pos, val);
else
change(id * 2 + 1, mid + 1, r, pos, val);
update(id);
}
}
// [ql, qr]表示查询的区间
info query(int id, int l, int r, int ql, int qr)
{
if (l == ql && r == qr)
return seg[id].val;
int mid = (l + r) / 2;
// [l, mid] , [mid + 1, r]
if (qr <= mid)
return query(id * 2, l, mid, ql, qr);
else if (ql > mid)
return query(id * 2 + 1, mid + 1, r, ql, qr);
else
{
// qr > mid, ql <= mid
// [ql, mid], [mid + 1, qr]
return query(id * 2, l, mid, ql, mid) +
query(id * 2 + 1, mid + 1, r, mid + 1, qr);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
build(1, 1, n);
while (q--)
{
int ty;
cin >> ty;
if (ty == 1)
{
int x, d;
cin >> x >> d;
change(1, 1, n, x, d);
}
else
{
int l, r;
cin >> l >> r;
auto ans = query(1, 1, n, l, r);
cout << ans.minv << " " << ans.mincnt << endl;
}
}
return 0;
}
第二题:
做法:这里单点修改还是跟上题一样,后面求最大子段和就有点麻烦了,需要讨论四种情况。
代码如下:
/*
coder:sunshine
school:njupt
*/
#include <bits/stdc++.h>
using namespace std;
#define endl '\n' //交互题删掉
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int N = 2e5 + 100;
int n, q;
int a[N];
struct info
{
ll mss, mpre, msuf, s;
info() {}
info(int a) : mss(a), mpre(a), msuf(a), s(a) {}
};
info operator+(const info &l, const info &r)
{
info a;
a.mss = max({l.mss, r.mss, l.msuf + r.mpre});
a.mpre = max(l.mpre, l.s + r.mpre);
a.msuf = max(r.msuf, r.s + l.msuf);
a.s = l.s + r.s;
return a;
}
struct node
{
info val;
/* data */
} seg[N * 4];
void update(int id)
{
seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}
void build(int id, int l, int r)
{
if (l == r)
{
seg[id].val = info(a[l]);
}
else
{
int mid = (l + r) / 2;
build(id * 2, l, mid);
build(id * 2 + 1, mid + 1, r);
update(id);
}
}
// 节点为id,对应的区间为[l, r],修改a[pos] -> val
void change(int id, int l, int r, int pos, int val)
{
if (l == r)
{
seg[id].val = info(val);
}
else
{
int mid = (l + r) / 2;
if (pos <= mid)
change(id * 2, l, mid, pos, val);
else
change(id * 2 + 1, mid + 1, r, pos, val);
update(id);
}
}
// [ql, qr]表示查询的区间
info query(int id, int l, int r, int ql, int qr)
{
if (l == ql && r == qr)
return seg[id].val;
int mid = (l + r) / 2;
// [l, mid] , [mid + 1, r]
if (qr <= mid)
return query(id * 2, l, mid, ql, qr);
else if (ql > mid)
return query(id * 2 + 1, mid + 1, r, ql, qr);
else
{
// qr > mid, ql <= mid
// [ql, mid], [mid + 1, qr]
return query(id * 2, l, mid, ql, mid) +
query(id * 2 + 1, mid + 1, r, mid + 1, qr);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
build(1, 1, n);
while (q--)
{
int ty;
cin >> ty;
if (ty == 1)
{
int x, d;
cin >> x >> d;
change(1, 1, n, x, d);
}
else
{
int l, r;
cin >> l >> r;
auto ans = query(1, 1, n, l, r);
cout << ans.mss << endl;
}
}
return 0;
}
第三题:线段树打标记1
做法:这是一个带懒标记的模板题,即区间修改,代码如下,有两份:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 201000;
int n, q;
int a[N];
struct info {
ll maxv;
};
struct tag {
ll add;
};
info operator + (const info &l, const info &r) {
return {max(l.maxv, r.maxv)};
}
info operator + (const info &v, const tag &t) {
return {v.maxv + t.add};
}
tag operator + (const tag &t1, const tag &t2) {
return {t1.add + t2.add};
}
struct node {
tag t;
info val;
} seg[N * 4];
// [l, r]
void update(int id) {
seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}
void settag(int id, tag t) {
seg[id].val = seg[id].val + t;
seg[id].t = seg[id].t + t;
}
void pushdown(int id) {
if (seg[id].t.add != 0) { // 标记非空
settag(id * 2, seg[id].t);
settag(id * 2 + 1, seg[id].t);
seg[id].t.add = 0;
}
}
void build(int id, int l, int r) {
if (l == r) {
seg[id].val = {a[l]};
} else {
int mid = (l + r) / 2;
build(id * 2, l, mid);
build(id * 2 + 1, mid + 1, r);
update(id);
}
}
// 节点为id,对应的区间为[l, r],修改a[pos] -> val
void modify(int id, int l, int r, int ql, int qr, tag t) {
if (l == ql && r == qr) {
settag(id, t);
return;
}
int mid = (l + r) / 2;
// 重要‼️
pushdown(id);
if (qr <= mid) modify(id * 2, l, mid, ql, qr, t);
else if (ql > mid) modify(id * 2 + 1, mid + 1, r, ql,qr, t);
else {
modify(id * 2, l, mid, ql, mid, t);
modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
}
// 重要‼️
update(id);
}
// [ql, qr]表示查询的区间
info query(int id, int l, int r, int ql, int qr) {
if (l == ql && r == qr) return seg[id].val;
int mid = (l + r) / 2;
// 重要‼️
pushdown(id);
if (qr <= mid) return query(id * 2, l, mid, ql, qr);
else if (ql > mid) return query(id * 2 + 1, mid + 1, r, ql,qr);
else {
// qr > mid, ql <= mid
// [ql, mid], [mid + 1, qr]
return query(id * 2, l, mid, ql, mid) +
query(id * 2 + 1, mid + 1, r, mid + 1, qr);
}
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
build(1, 1, n);
for (int i = 0; i < q; i++) {
int ty;
scanf("%d", &ty);
if (ty == 1) {
int l, r, d;
scanf("%d%d%d", &l, &r, &d);
modify(1, 1, n, l, r, (tag){d});
} else {
int l, r;
scanf("%d%d", &l, &r);
auto ans = query(1, 1, n, l, r);
printf("%lld\n", ans.maxv);
}
}
}
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 201000;
int n, q;
int a[N];
struct node {
ll t, val;
} seg[N * 4];
// [l, r]
void update(int id) {
seg[id].val = max(seg[id * 2].val, seg[id * 2 + 1].val);
}
void settag(int id, ll t) {
seg[id].val = seg[id].val + t;
seg[id].t = seg[id].t + t;
}
void pushdown(int id) {
if (seg[id].t != 0) { // 标记非空
settag(id * 2, seg[id].t);
settag(id * 2 + 1, seg[id].t);
seg[id].t = 0;
}
}
void build(int id, int l, int r) {
if (l == r) {
seg[id].val = {a[l]};
} else {
int mid = (l + r) / 2;
build(id * 2, l, mid);
build(id * 2 + 1, mid + 1, r);
update(id);
}
}
// 节点为id,对应的区间为[l, r],修改a[pos] -> val
void modify(int id, int l, int r, int ql, int qr, ll t) {
if (l == ql && r == qr) {
settag(id, t);
return;
}
int mid = (l + r) / 2;
// 重要‼️
pushdown(id);
if (qr <= mid) modify(id * 2, l, mid, ql, qr, t);
else if (ql > mid) modify(id * 2 + 1, mid + 1, r, ql,qr, t);
else {
modify(id * 2, l, mid, ql, mid, t);
modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
}
// 重要‼️
update(id);
}
// [ql, qr]表示查询的区间
ll query(int id, int l, int r, int ql, int qr) {
if (l == ql && r == qr) return seg[id].val;
int mid = (l + r) / 2;
// 重要‼️
pushdown(id);
if (qr <= mid) return query(id * 2, l, mid, ql, qr);
else if (ql > mid) return query(id * 2 + 1, mid + 1, r, ql,qr);
else {
// qr > mid, ql <= mid
// [ql, mid], [mid + 1, qr]
return max(query(id * 2, l, mid, ql, mid),
query(id * 2 + 1, mid + 1, r, mid + 1, qr));
}
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
build(1, 1, n);
for (int i = 0; i < q; i++) {
int ty;
scanf("%d", &ty);
if (ty == 1) {
int l, r, d;
scanf("%d%d%d", &l, &r, &d);
modify(1, 1, n, l, r, d);
} else {
int l, r;
scanf("%d%d", &l, &r);
auto ans = query(1, 1, n, l, r);
printf("%lld\n", ans);
}
}
}
第四道:线段树打标记2
做法:也是一道基础题,虽然比上一道稍难,但也偏模板,代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 201000;
const ll mod = 1000000007;
int n, q;
int a[N];
struct tag {
ll mul, add;
};
tag operator + (const tag &t1, const tag &t2) {
// (x * t1.mul + t1.add) * t2.mul + t2.add
return {t1.mul * t2.mul % mod, (t1.add * t2.mul + t2.add) % mod};
}
struct node {
tag t;
ll val;
int sz;
} seg[N * 4];
// [l, r]
void update(int id) {
seg[id].val = (seg[id * 2].val + seg[id * 2 + 1].val) % mod;
}
void settag(int id, tag t) {
seg[id].t = seg[id].t + t;
seg[id].val = (seg[id].val * t.mul + seg[id].sz * t.add) % mod;
}
void pushdown(int id) {
if (seg[id].t.mul != 1 || seg[id].t.add != 0) { // 标记非空
settag(id * 2, seg[id].t);
settag(id * 2 + 1, seg[id].t);
seg[id].t.mul = 1;
seg[id].t.add = 0;
}
}
void build(int id, int l, int r) {
seg[id].t = {1, 0};
seg[id].sz = r - l + 1;
if (l == r) {
seg[id].val = {a[l]};
} else {
int mid = (l + r) / 2;
build(id * 2, l, mid);
build(id * 2 + 1, mid + 1, r);
update(id);
}
}
// 节点为id,对应的区间为[l, r],修改a[pos] -> val
void modify(int id, int l, int r, int ql, int qr, tag t) {
if (l == ql && r == qr) {
settag(id, t);
return;
}
int mid = (l + r) / 2;
// 重要‼️
pushdown(id);
if (qr <= mid) modify(id * 2, l, mid, ql, qr, t);
else if (ql > mid) modify(id * 2 + 1, mid + 1, r, ql,qr, t);
else {
modify(id * 2, l, mid, ql, mid, t);
modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
}
// 重要‼️
update(id);
}
// [ql, qr]表示查询的区间
ll query(int id, int l, int r, int ql, int qr) {
if (l == ql && r == qr) return seg[id].val;
int mid = (l + r) / 2;
// 重要‼️
pushdown(id);
if (qr <= mid) return query(id * 2, l, mid, ql, qr);
else if (ql > mid) return query(id * 2 + 1, mid + 1, r, ql,qr);
else {
// qr > mid, ql <= mid
// [ql, mid], [mid + 1, qr]
return (query(id * 2, l, mid, ql, mid) +
query(id * 2 + 1, mid + 1, r, mid + 1, qr)) % mod;
}
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
build(1, 1, n);
for (int i = 0; i < q; i++) {
int ty;
scanf("%d", &ty);
if (ty <= 3) {
int l, r, d;
scanf("%d%d%d", &l, &r, &d);
if (ty == 1) modify(1, 1, n, l, r, (tag){1, d});
else if (ty == 2) modify(1, 1, n, l, r, (tag){d, 0});
else modify(1, 1, n, l, r, (tag){0, d});
} else {
int l, r;
scanf("%d%d", &l, &r);
auto ans = query(1, 1, n, l, r);
printf("%lld\n", ans);
}
}
}
题目5:线段树上二分
做法:线段树+二分,我还没遇到过,可能是我触及不到的领域
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 201000;
int n, q;
int a[N];
struct node {
int val;
} seg[N * 4];
// [l, r]
void update(int id) {
seg[id].val = max(seg[id * 2].val, seg[id * 2 + 1].val);
}
void build(int id, int l, int r) {
if (l == r) {
seg[id].val = a[l];
} else {
int mid = (l + r) / 2;
build(id * 2, l, mid);
build(id * 2 + 1, mid + 1, r);
update(id);
}
}
// 节点为id,对应的区间为[l, r],修改a[pos] -> val
void change(int id, int l, int r, int pos, int val) {
if (l == r) {
seg[id].val = val;
} else {
int mid = (l + r) / 2;
if (pos <= mid) change(id * 2, l, mid, pos, val);
else change(id * 2 + 1, mid + 1, r, pos, val);
// 重要‼️
update(id);
}
}
int search(int id, int l, int r, int ql, int qr, int d) {
if (l == ql && r == qr) {
if (seg[id].val < d) return -1;
else {
if (l == r) return l;
int mid = (l + r) / 2;
if (seg[id * 2].val >= d) return search(id * 2, l, mid, ql, mid, d);
else return search(id * 2 + 1, mid + 1, r, mid + 1, qr, d);
}
}
int mid = (l + r) / 2;
// [l, mid] , [mid + 1, r]
if (qr <= mid) return search(id * 2, l, mid, ql, qr, d);
else if (ql > mid) return search(id * 2 + 1, mid + 1, r, ql, qr, d);
else {
int pos = search(id * 2, l, mid, ql, mid, d);
if (pos == -1) return search(id * 2 + 1, mid + 1, r, mid + 1, qr, d);
else return pos;
}
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
build(1, 1, n);
for (int i = 0; i < q; i++) {
int ty;
scanf("%d", &ty);
if (ty == 1) {
int x, d;
scanf("%d%d", &x, &d);
change(1, 1, n, x, d);
} else {
int l, r, d;
scanf("%d%d%d", &l, &r, &d);
auto ans = search(1, 1, n, l, r, d);
printf("%d\n", ans);
}
}
}