[AcWing面向模型编程]线段树

线段树

题目1(单点修改,区间最值):Acwing1275. 最大数

给定一个正整数数列 a1,a2,…,ana1,a2,…,an,每一个数都在 0∼p−1 之间。

可以对这列数进行两种操作:

  1. 添加操作:向序列后添加一个数,序列长度变成 n+1;
  2. 询问操作:询问这个序列中最后 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,ba,cb,...)
用差分将区间修改转换成单点修改。注意:修改的是差分,因为求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

  1. lazyadd = (lazyadd * mul + add) % mod
  2. 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);
	}
}

线段树复习完毕!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是acwing模板线段的示例代码: ```cpp const int N = 100010; int n, m; int a[N]; struct Node { int l, r; int v, lazy; } tree[N * 4]; void pushup(int x) { tree[x].v = tree[x * 2].v + tree[x * 2 + 1].v; } void pushdown(int x) { if (tree[x].lazy) { int l = tree[x].l, r = tree[x].r; int mid = (l + r) >> 1; tree[x * 2].v += tree[x].lazy * (mid - l + 1); tree[x * 2 + 1].v += tree[x].lazy * (r - mid); tree[x * 2].lazy += tree[x].lazy; tree[x * 2 + 1].lazy += tree[x].lazy; tree[x].lazy = 0; } } void build(int x, int l, int r) { tree[x].l = l, tree[x].r = r; if (l == r) { tree[x].v = a[l]; return; } int mid = (l + r) >> 1; build(x * 2 l, mid); build(x * 2 + 1, mid +1, r); pushup(x); } void modify(int x, int l, int r, int val) { if (tree[x].l >= l && tree[x].r <= r) { tree[x].v += val * (tree[x].r - tree[x].l + 1); tree[x].lazy += val; return; } pushdown(x); int mid = (tree[x].l + tree[x].r) >> 1; if (l <= mid) modify(x * 2, l, r, val); if (r > mid) modify(x * 2 + 1, l, r, val); pushup(x); } int query(int x, int l, int r) { if (tree[x].l >= l && tree[x].r <= r) { return tree[x].v; } pushdown(x); int mid = (tree[x].l + tree[x].r) >> 1; int sum = 0; if (l <= mid) sum += query(x * 2, l, r); if (r > mid) sum += query(x * 2 + 1, l, r); return sum; } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> a[i]; } build(1, 1, n); while (m--) { int op, l, r, val; cin >> op; if (op == 1) { cin >> l >> r >> val; modify(1, l, r, val); } else if (op == 2) { cin >> l >> r; cout << query(1, l, r) << endl; } } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值