线段树
题目1(单点修改,区间最值):Acwing1275. 最大数
给定一个正整数数列 a1,a2,…,ana1,a2,…,an,每一个数都在 0∼p−1 之间。
可以对这列数进行两种操作:
- 添加操作:向序列后添加一个数,序列长度变成 n+1;
- 询问操作:询问这个序列中最后 L 个数中最大的数是多少。
程序运行的最开始,整数序列为空。
写一个程序,读入操作的序列,并输出询问操作的答案。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;//三年竞赛一场空,不开long long见祖宗
//typedef __int128 lll;
#define print(i) cout << "debug: " << i << endsl
#define close() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(a, b) memset(a, b, sizeof(a))
#define pb(a) push_back(a)
#define x first
#define y second
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
const int maxn = 2e5 + 10;
const int inf = 0x3f3f3f3f;
struct node
{
int l, r;
int maxv;
}t[maxn << 2];
void pushup(int tar)
{
t[tar].maxv = max(t[tar << 1].maxv, t[tar << 1 | 1].maxv);
}
void build(int tar, int l, int r)
{
t[tar] = {l, r, 0};
if(l == r) return;
int mid = l + r >> 1;
build(tar << 1, l, mid), build(tar << 1 | 1, mid + 1, r);
}
void update(int tar, int pos, int val)
{
if(t[tar].l == t[tar].r)
{
t[tar].maxv = val;
return;
}
int mid = t[tar].l + t[tar].r >> 1;
if(pos <= mid) update(tar << 1, pos, val);
else update(tar << 1 | 1, pos, val);
pushup(tar);
}
int query(int tar, int L, int R)
{
if(L <= t[tar].l && t[tar].r <= R) return t[tar].maxv;
int mid = t[tar].l + t[tar].r >> 1;
int maxx = 0;
if(L <= mid) maxx = max(maxx, query(tar << 1, L, R));
if(R > mid) maxx = max(maxx, query(tar << 1 | 1, L, R));
return maxx;
}
int main()
{
int m, p; scanf("%d%d", &m, &p);
build(1, 1, m);
int last = 0, n = 0;
while(m--)
{
char ope[2];
int val;
scanf("%s%d", ope, &val);
if(ope[0] =='Q')
printf("%d\n", (last = query(1, n - val + 1, n)));
else
update(1, ++n, (last + val) % p);
}
}
题目2(单点修改,区间合并):AcWing 245. 你能回答这些问题吗(求最大连续子段和)
给定长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:
1、“1 x y”,查询区间 [x,y] 中的最大连续子段和,即 maxx≤l≤r≤ymaxx≤l≤r≤y{∑ri=lA[i]∑i=lrA[i]}。
2、“2 x y”,把 A[x] 改成 y。
对于每个查询指令,输出一个整数表示答案。
输入格式
第一行两个整数N,M。
第二行N个整数A[i]。
接下来M行每行3个整数k,x,y,k=1表示查询(此时如果x>y,请交换x,y),k=2表示修改。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;//三年竞赛一场空,不开long long见祖宗
//typedef __int128 lll;
#define print(i) cout << "debug: " << i << endsl
#define close() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(a, b) memset(a, b, sizeof(a))
#define pb(a) push_back(a)
#define x first
#define y second
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
const int maxn = 5e5 + 10;
const int inf = 0x3f3f3f3f;
struct node
{
int l, r;
int sum, lmax, rmax, smax;
}t[maxn << 2];
int a[maxn];
void pushup(node& now, node& lson, node& rson)
{
now.sum = lson.sum + rson.sum;
now.lmax = max(lson.lmax, lson.sum + rson.lmax);
now.rmax = max(rson.rmax, rson.sum + lson.rmax);
now.smax = max(max(lson.smax, rson.smax), lson.rmax + rson.lmax);
}
void pushup(int tar)
{
pushup(t[tar], t[tar << 1], t[tar << 1 | 1]);
}
void build(int tar, int l, int r)
{
if(l == r)
{
t[tar] = {l, r, a[l], a[l], a[l], a[l]};
return;
}
t[tar] = {l, r};
int mid = l + r >> 1;
build(tar << 1, l, mid), build(tar << 1 | 1, mid + 1, r);
pushup(tar);
}
void update(int tar, int pos, int val)
{
if(t[tar].l == t[tar].r)
{
t[tar] = {t[tar].l, t[tar].r, val, val, val, val};
return;
}
int mid = t[tar].l + t[tar].r >> 1;
if(pos <= mid) update(tar << 1, pos, val);
else update(tar << 1 | 1, pos, val);
pushup(tar);
}
node query(int tar, int L, int R)
{
if(L <= t[tar].l && t[tar].r <= R)
return t[tar];
int mid = t[tar].l + t[tar].r >> 1;
if(R <= mid) return query(tar << 1, L, R);
else if(L > mid) return query(tar << 1 | 1, L, R);
else
{
auto l = query(tar << 1, L, R);
auto r = query(tar << 1 | 1, L, R);
node res;
pushup(res, l, r);
return res;
}
}
int main()
{
int n, q; cin >> n >> q;
for(int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
while(q--)
{
int ope, a, b; cin >> ope >> a >> b;
if(ope == 1)
{
if(a > b) swap(a, b);
cout << query(1, a, b).smax << endl;
}
else update(1, a, b);
}
}
题目3:AcWing 246. 区间最大公约数(算法提高课)
分析:
结论:
g
c
d
(
a
,
b
,
c
.
.
.
)
=
g
c
d
(
a
,
b
−
a
,
c
−
b
,
.
.
.
)
gcd(a, b, c...) = gcd(a, b - a, c - b, ...)
gcd(a,b,c...)=gcd(a,b−a,c−b,...)
用差分将区间修改转换成单点修改。注意:修改的是差分,因为求gcd只用到差分。
g
c
d
(
a
,
d
i
f
b
,
d
i
f
c
,
.
.
.
)
=
g
c
d
(
a
,
(
g
c
d
(
d
i
f
b
,
d
i
f
c
,
.
.
.
)
)
)
gcd(a, difb, difc,...) = gcd(a, (gcd(difb, difc,...)))
gcd(a,difb,difc,...)=gcd(a,(gcd(difb,difc,...)))
g
c
d
(
a
)
gcd(a)
gcd(a)可以通过树状数组或者线段树求差分和得出,gcd(difb, difc,…)可以通过线段树区间查询得到
易错点:l=r的时候,更新时要特判(详见代码)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;//三年竞赛一场空,不开long long见祖宗
//typedef __int128 lll;
#define print(i) cout << "debug: " << i << endsl
#define close() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(a, b) memset(a, b, sizeof(a))
#define pb(a) push_back(a)
#define x first
#define y second
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
const int maxn = 5e5 + 10;
const int inf = 0x3f3f3f3f;
struct node
{
int l, r;
ll sum, d;
}t[maxn << 2];
ll a[maxn];
ll gcd(ll a, ll b)
{
return !b ? a : gcd(b, a % b);
}
void pushup(node& now, node& lson, node& rson)
{
now.sum = lson.sum + rson.sum;
now.d = gcd(lson.d, rson.d);
}
void pushup(int tar)
{
pushup(t[tar], t[tar << 1], t[tar << 1 | 1]);
}
void build(int tar, int l, int r)
{
t[tar] = {l, r};
if(l == r)
{
t[tar].sum = t[tar].d = a[l] - a[l - 1];
return;
}
int mid = l + r >> 1;
build(tar << 1, l, mid), build(tar << 1 | 1, mid + 1, r);
pushup(tar);
}
void update(int tar, int pos, ll val)
{
if(t[tar].l == t[tar].r)
{
t[tar].sum += val, t[tar].d = t[tar].sum;
return;
}
int mid = t[tar].l + t[tar].r >> 1;
if(pos <= mid) update(tar << 1, pos, val);
else update(tar << 1 | 1, pos, val);
pushup(tar);
}
node query(int tar, int L, int R)
{
if(L <= t[tar].l && t[tar].r <= R) return t[tar];
int mid = t[tar].l + t[tar].r >> 1;
if(R <= mid) return query(tar << 1, L, R);
else if(L > mid) return query(tar << 1 | 1, L, R);
else
{
node lson = query(tar << 1, L, R), rson = query(tar << 1 | 1, L, R), res;
pushup(res, lson, rson);
return res;
}
}
int main()
{
int n, m; cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
while(m--)
{
char ope[2];
ll l, r, d;
scanf("%s%lld%lld", ope, &l, &r);
if(ope[0] == 'Q')
{
ll sum = query(1, 1, l).sum;
if(l < r) sum = gcd(sum, query(1, l + 1, r).d);
cout << abs(sum) << endl;
}
else
{
scanf("%lld", &d);
update(1, l, d);
if(r + 1 <= n) update(1, r + 1, -d);//易错点
}
}
}
题目4(扫描线+离散化模板):AcWing 247. 亚特兰蒂斯
分析:
扫描线板子
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;//三年竞赛一场空,不开long long见祖宗
//typedef __int128 lll;
#define print(i) cout << "debug: " << i << endsl
#define close() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(a, b) memset(a, b, sizeof(a))
#define pb(a) push_back(a)
// #define x first
// #define y second
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
struct edge
{
double x, y1, y2;
int flag;
bool operator< (const edge& b) const
{
return x < b.x;
}
}e[maxn << 1];
struct node
{
int l, r;
int cnt;
double len;
}t[maxn << 3];
double y[maxn << 1];
int tot;
int n;
int getid(double x)
{
return lower_bound(y + 1, y + 1 + tot, x) - y;
}
void pushup(int tar)
{
if(t[tar].cnt) t[tar].len = y[t[tar].r + 1] - y[t[tar].l];
else if(t[tar].l == t[tar].r) t[tar].len = 0;
else t[tar].len = t[tar << 1].len + t[tar << 1 | 1].len;
}
void build(int tar, int l, int r)
{
t[tar] = {l, r, 0, 0};
if(l == r) return;
int mid = l + r >> 1;
build(tar << 1, l, mid), build(tar << 1 | 1, mid + 1, r);
}
void update(int tar, int L, int R, int val)
{
if(L <= t[tar].l && t[tar].r <= R)
{
t[tar].cnt += val;
pushup(tar);
return;
}
int mid = t[tar].l + t[tar].r >> 1;
if(L <= mid) update(tar << 1, L, R, val);
if(R > mid) update(tar << 1 | 1, L, R, val);
pushup(tar);
}
int main()
{
int cases = 0;
while(cin >> n && n)
{
for(int i = 1; i <= n; i++)
{
double x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
e[i * 2 - 1] = {x1, y1, y2, 1}, e[i * 2] = {x2, y1, y2, -1};
y[i * 2 - 1] = y1, y[i * 2] = y2;
}
sort(y + 1, y + 1 + 2 * n);
tot = unique(y + 1, y + 1 + 2 * n) - y - 1;
sort(e + 1, e + 1 + 2 * n);
build(1, 1, tot - 1);//注意:线段树维护的点的数目 = 区间的个数 - 1
double res = 0;
for(int i = 1; i <= 2 * n; i++)
{
res += t[1].len * (e[i].x - e[i - 1].x);
update(1, getid(e[i].y1), getid(e[i].y2) - 1, e[i].flag);
}
printf("Test case #%d\nTotal explored area: %.2f\n\n", ++cases, res);
}
}
题目5:1277. 维护序列
分析:
题意:
老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。
有长为 N 的数列,不妨设为 a1,a2,…,aN。
有如下三种操作形式:
把数列中的一段数全部乘一个值;
把数列中的一段数全部加一个值;
询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模 P 的值。
维护两个懒惰标记,每次操作可以看成先乘mul,后加add
- lazyadd = (lazyadd * mul + add) % mod
- lazymul = lazymul * mul % mod
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;//三年竞赛一场空,不开long long见祖宗
//typedef __int128 lll;
#define print(i) cout << "debug: " << i << endsl
#define close() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(a, b) memset(a, b, sizeof(a))
#define pb(a) push_back(a)
#define x first
#define y second
typedef pair<int, int> pii;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
struct node
{
int l, r;
ll mul, add, sum;
}t[maxn << 2];
int n, mod;
ll a[maxn];
void pushup(int tar)
{
t[tar].sum = (t[tar << 1].sum + t[tar << 1 | 1].sum) % mod;
}
void pushdown(node& now, ll mul, ll add)
{
now.sum = (now.sum * mul + (now.r - now.l + 1) * add) % mod;
now.mul = now.mul * mul % mod;
now.add = (now.add * mul + add) % mod;
}
void pushdown(int tar)
{
pushdown(t[tar << 1], t[tar].mul, t[tar].add);
pushdown(t[tar << 1 | 1], t[tar].mul, t[tar].add);
t[tar].mul = 1, t[tar].add = 0;
}
void build(int tar, int l, int r)
{
t[tar] = {l, r, 1, 0, a[l]};
if(l == r) return;
int mid = l + r >> 1;
build(tar << 1, l, mid), build(tar << 1 | 1, mid + 1, r);
pushup(tar);
}
void update(int tar, int L, int R, ll mul, ll add)
{
if(L <= t[tar].l && t[tar].r <= R)
{
pushdown(t[tar], mul, add);
return;
}
pushdown(tar);
int mid = t[tar].r + t[tar].l >> 1;
if(L <= mid) update(tar << 1, L, R, mul, add);
if(R > mid) update(tar << 1 | 1, L, R, mul, add);
pushup(tar);
}
ll query(int tar, int L, int R)
{
if(L <= t[tar].l && t[tar].r <= R)
return t[tar].sum;
pushdown(tar);
int mid = t[tar].l + t[tar].r >> 1;
ll res = 0;
if(L <= mid) res += query(tar << 1, L, R), res %= mod;
if(R > mid) res += query(tar << 1 | 1, L, R), res %= mod;
return res;
}
int main()
{
cin >> n >> mod;
for(int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
int q; cin >> q;
while(q--)
{
int ope, l, r;
ll val;
cin >> ope >> l >> r;
if(ope == 3)
{
cout << query(1, l, r) << endl;
continue;
}
cin >> val;
if(ope == 1) update(1, l, r, val, 0);
else update(1, l, r, 1, val);
}
}