线段树题解

A.【例题1】求区间和

单修区查,直接线段树即可。

当然也可以树状数组+差分。

Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;

const int N = 1e5 + 10;
int n, q, a[N];

struct node{
	int val;
	int l, r;
	int lzy;
}t[N << 2];

void pushup(int u) {
	t[u].val = t[ls].val + t[rs].val;
}
void maketag(int u, int x) {
	t[u].lzy += x;
	t[u].val += (t[u].r - t[u].l + 1) * x;
}
void pushdown(int u) {
	if (t[u].lzy) {
		maketag(ls, t[u].lzy);
		maketag(rs, t[u].lzy);
		t[u].lzy = 0;
	}
}
void build(int u, int l, int r) {
	t[u].val = t[u].lzy = 0;
	t[u].l = l, t[u].r = r;
	if (l == r) {
		t[u].val = a[l];
		return ;
	}
	int M = (l + r) >> 1;
	build(ls, l, M);
	build(rs, M + 1, r);
	pushup(u);
}
void modify(int u, int l, int r, int k) {
	if (l <= t[u].l && t[u].r <= r) maketag(u, k);
	else {
		pushdown(u);
		int M = (t[u].l + t[u].r) >> 1;
		if (l <= M) modify(ls, l, r, k);
		if (r > M) modify(rs, l, r, k);
		pushup(u);
	}
}
int query(int u, int l, int r) {
	if (l <= t[u].l && t[u].r <= r) return t[u].val;
	pushdown(u);
	int M = (t[u].l + t[u].r) >> 1, res = 0;
	if (l <= M) res += query(ls, l, r);
	if (r > M) res += query(rs, l, r);
	return res;
}

signed main() {
	scanf("%lld%lld", &n, &q);
	for (int i = 1; i <= n; i++) {
		a[i] = 0;
	}
	build(1, 1, n);
	while (q--) {
		int op, l, r;
		scanf("%lld%lld%lld", &op, &l, &r);
		if (op == 0) {
			modify(1, l, l, r);
		} else {
			printf("%lld\n", query(1, l, r));
		}
	}
	return 0;
}

B.【例题2】区间查改

区修区查板子,当然依旧可以树状数组。

Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;

template <typename T = int> 
T rd() {
    T s = 0;
    int f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        s = (s << 3) + (s << 1) + ch - 48;
        ch = getchar();
    }
    return s * f;
}

const int N = 1e6 + 10;
int n, q, a[N];

struct node{
	int val;
	int l, r;
	int lzy;
}t[N << 2];

void pushup(int u) {
	t[u].val = t[ls].val + t[rs].val;
}
void maketag(int u, int x) {
	t[u].lzy += x;
	t[u].val += (t[u].r - t[u].l + 1) * x;
}
void pushdown(int u) {
	if (t[u].lzy) {
		maketag(ls, t[u].lzy);
		maketag(rs, t[u].lzy);
		t[u].lzy = 0;
	}
}
void build(int u, int l, int r) {
	t[u].val = t[u].lzy = 0;
	t[u].l = l, t[u].r = r;
	if (l == r) {
		t[u].val = a[l];
		return ;
	}
	int M = (l + r) >> 1;
	build(ls, l, M);
	build(rs, M + 1, r);
	pushup(u);
}
void modify(int u, int l, int r, int k) {
	if (l <= t[u].l && t[u].r <= r) maketag(u, k);
	else {
		pushdown(u);
		int M = (t[u].l + t[u].r) >> 1;
		if (l <= M) modify(ls, l, r, k);
		if (r > M) modify(rs, l, r, k);
		pushup(u);
	}
}
int query(int u, int l, int r) {
	if (l <= t[u].l && t[u].r <= r) return t[u].val;
	pushdown(u);
	int M = (t[u].l + t[u].r) >> 1, res = 0;
	if (l <= M) res += query(ls, l, r);
	if (r > M) res += query(rs, l, r);
	return res;
}

signed main() {
	n = rd(), q = rd();
	for (int i = 1; i <= n; i++) {
		a[i] = rd();
	}
	build(1, 1, n);
	while (q--) {
		int op = rd(), l = rd(), r = rd();
		if (op == 1) {
			int k = rd();
			modify(1, l, r, k);
		} else {
			printf("%lld\n", query(1, l, r));
		}
	}
	return 0;
}

C.【例题3】公园遛狗

维护四个值:区间和,区间最大子段和,最大前缀和,最大后缀和

接下来我们重点考虑合并操作(即 pushup 操作)。

区间和的合并只需要将两个区间的区间和加起来即可。

区间最大前缀和:max(区间的最大前缀和,区间的和+区间的最大前缀和)

容易证明这样一定是最大的,证明如下:

我们考虑区间前缀和右端点的位置。

如果在左区间内,那么显然取左区间最大前缀和是最优的。

如果在右区间内,那么我们必然选取左区间的全部,这部分权值是定值,那么在右区间的部分选取最大前缀和也是最优的。

证毕。

区间最大后缀和同理。

区间中最大子段和:

首先先选取 max(区间的最大前缀和,区间的最大子段),这是显然的。

接下来,如果区间的最大后缀和区间的最大前缀和有一个小于 0,那么就选取两个的最大值,否则就选取两者的和。这容易证明,证明略。

最后为两者的最大值。

合并操作代码:

//l是区间左端点,r是区间右端点
//mx是最大子段和,ml是最大前缀和,mr是最大后缀和
//val是区间和
inline node solve(node a, node b) {
	node res;
	res.l = a.l, res.r = b.r;
	res.val = a.val + b.val;
	if ((a.mr < 0) || (b.ml < 0)) {
		res.mx = max(a.mr, b.ml);
	} else {
		res.mx = a.mr + b.ml;
	}
	res.mx = max(res.mx, a.mx);
	res.mx = max(res.mx, b.mx);
	res.ml = max(a.ml, a.val + b.ml);
	res.mr = max(b.mr, b.val + a.mr);
	return res;
}

好了,这题基本上就做完了,小细节是 query 函数返回值应该写一个 node

Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;

const int N = 1e6 + 10;
int n, q, a[N];

struct node{
	int val;
	int l, r; 
	int ml, mr;
	int mx;
}t[N << 2];

inline int rd() {
    int s = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9') {
		if (ch == '-') f = -1; 
		ch = getchar();
	}
    while ('0' <= ch && ch <= '9') {
		s = (s << 3) + (s << 1) + ch - 48; 
		ch = getchar();
	}
    return s * f;
}
inline node solve(node a, node b) {
	node res;
	res.l = a.l, res.r = b.r;
	res.val = a.val + b.val;
	if ((a.mr < 0) || (b.ml < 0)) {
		res.mx = max(a.mr, b.ml);
	} else {
		res.mx = a.mr + b.ml;
	}
	res.mx = max(res.mx, a.mx);
	res.mx = max(res.mx, b.mx);
	res.ml = max(a.ml, a.val + b.ml);
	res.mr = max(b.mr, b.val + a.mr);
	return res;
}
inline void pushup(const int u) {
	node k = solve(t[ls], t[rs]);
	t[u] = k;
}
void build(const int u, int l, int r) {
	t[u].l = l, t[u].r = r;
	if (l == r) {
    	t[u].val = t[u].ml = t[u].mr = t[u].mx = a[l];
		return ;
	}
	int M = (l + r) >> 1;
	build(ls, l, M);
	build(rs, M + 1, r);
	pushup(u);
}
node query(const int u, int l, int r) {
    if (l <= t[u].l && r >= t[u].r) return t[u];
    int M = (t[u].l + t[u].r) >> 1;
    if (l <= M && r > M) {
    	node x = query(ls, l, r);
    	node y = query(rs, l, r);
    	return solve(x, y);
	}
    else if (l <= M) return query(ls, l, r);
    else return query(rs, l, r);
}
void modify(const int u, int p, int k) {
    if (t[u].l == t[u].r) {
    	t[u].val = t[u].ml = t[u].mr = t[u].mx = k;
    	return;
	}
    int M = (t[u].l + t[u].r) >> 1;
    if (p <= M) modify(ls, p, k);
    else modify(rs, p, k);
    pushup(u);
}

signed main() {
	n = rd(), q = rd();
	for (int i = 1; i <= n; i++) {
		a[i] = rd();
	}
	build(1, 1, n);
	while (q--) {
		int op = rd(), x = rd(), y = rd();
		if (op == 1) {
			if (x > y) swap(x, y);
			node ans = query(1, x, y);
			printf("%d\n", ans.mx);
		} else {
			modify(1, x, y);
		}
	}
    return 0;
}

D.【例题4】维护序列

板子题,打两个懒标记即可。

Code
#include <bits/stdc++.h>
#define ls u * 2
#define rs u * 2 + 1
#define int long long
using namespace std;

const int N = 1e5 + 10;
struct node{
	int val;
	int l, r; 
	int lza, lzm;
}t[N << 2];
int n, q, P, a[N];

inline int read() {
    int s = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9') {
		if (ch == '-') f = -1; 
		ch = getchar();
	}
    while ('0' <= ch && ch <= '9') {
		s = (s << 3) + (s << 1) + ch - 48; 
		ch = getchar();
	}
    return s * f;
}
inline void pushup(const int u) {
	t[u].val = (t[ls].val + t[rs].val) % P;
	return ;
}
inline void maketag(const int u, int x, int type) {
	if (type == 1) {
		t[u].lza = (t[u].lza * x) % P; 
		t[u].lzm = (t[u].lzm * x) % P;
		t[u].val = (t[u].val * x) % P;	
	} else {
		t[u].lza = (t[u].lza + x) % P;
		t[u].val = (t[u].val + (x * (t[u].r - t[u].l + 1) % P)) % P;
	}
	return ;
}
inline void pushdown(const int u) {
	maketag(ls, t[u].lzm, 1);
	maketag(rs, t[u].lzm, 1);
    maketag(ls, t[u].lza, 2);
    maketag(rs, t[u].lza, 2);
    t[u].lza = 0, t[u].lzm = 1;
    return ;
}
void build(const int u, int l, int r) {
	t[u].l = l, t[u].r = r, t[u].lza = 0, t[u].lzm = 1;
	if (l == r) {
		t[u].val = a[l];
		return ;
	}
	int M = (l + r) >> 1;
	build(ls, l, M);
	build(rs, M + 1, r);
	pushup(u);
	return ;
}
int query(const int u, int l, int r) {
    if (l <= t[u].l && r >= t[u].r) return t[u].val;
    pushdown(u);
    int M = (t[u].l + t[u].r) >> 1, sum = 0;
    if (l <= M) sum = (sum + query(ls, l, r)) % P;
    if (r > M) sum = (sum + query(rs, l, r)) % P;
    return sum % P; 
}
void modify(const int u, int l, int r, int k, int type) {
    if (l <= t[u].l && r >= t[u].r) maketag(u, k, type);
    else {
        pushdown(u);
        int M = (t[u].l + t[u].r) >> 1;
        if (l <= M) modify(ls, l, r, k, type);
        if (r > M) modify(rs, l, r, k, type);
        pushup(u);
    }
    return ;
}

signed main() {
	n = read(), P = read();
	for (int i = 1; i <= n; i++) a[i] = read();
	build(1, 1, n);
	q = read();
	while (q--) {
		int op, x, y, k;
		op = read();
		if (op == 1 || op == 2) {
			x = read(), y = read(), k = read();
			modify(1, x, y, k, op);
		} else {
			x = read(), y = read();
			printf("%lld\n", query(1, x, y) % P);
		}
	}
    return 0;
}


E.【例题5】字符串排序

定义函数 f(x),表示在字母表中排名为 x 的字母;g(c) 表示字母 c 在字母表中的排名。

由于只有 26 种字符,考虑对每种字符都建立一颗线段树,这样我们需要 26 颗线段树。

具体的,第 i 颗线段树维护字母 f(i) 区间内的出现次数。这样,根据线段树的叶子节点存储的信息可以计算每个位置的字符。

考虑排序操作,这里只讨论升序排序,因为降序排序同理即可。

我们观察一个例子:对于 acababc 进行排序,结果是 aaabbcc。这里,第 11+31 位置是 a,第 44+21 位置是 b,第 6 到第 6+21 位置是 c。注意到这里的 422 是这些字符在区间内的个数。于是我们就有了一种方案:

每次修改时,从小往大枚举字符,这样就可以保证升序。对于字符 i,我们将第 g(i) 颗线段树 p+1p+cnt+1 修改为 1,其他修改为 0。其中,p 为上一个字符的末尾,cnt 为这个字符在修改的区间内的出个数。这样就可以完成的排序的操作了。

这里,我们利用了两个性质:排序后相同的字符是连续的;排序操作不影响区间内字符的个数。

输出的时候,我们遍历时遍历到叶子节点输出即可。

时间复杂度 O(26 mlog(n))

其实此题与这个题有着较为相似的思路,只不过后者将值域从 26 扩大为正整数,需要二分答案,略难一些,有余力的读者可以尝试一下。

Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
using namespace std;

const int N = 1e5 + 10;
int n, m;
char a[N];

struct tree{
    int l, r;
    int val;
    int lzy;
}t[27][N << 4];

void pushup(int u, int c) {
    t[c][u].val = t[c][ls].val + t[c][rs].val;
}
void maketag(int u, int x, int c) {
    //cout << c << ' ' << x << endl;
    t[c][u].lzy = x;
    t[c][u].val = x * (t[c][u].r - t[c][u].l + 1); 
}
void pushdown(int u, int c) {
    if (t[c][u].lzy != -1) {
        maketag(ls, t[c][u].lzy, c);
        maketag(rs, t[c][u].lzy, c);
        t[c][u].lzy = -1;
    }
}
void build(int u, int l, int r) {
    for (int i = 0; i < 26; i++) {
        t[i][u].l = l, t[i][u].r = r, t[i][u].lzy = -1, t[i][u].val = 0; 
    }
    if (l == r) {
        t[a[l] - 'a'][u].val = 1;
        return ;
    }
    int M = (l + r) >> 1;
    build(ls, l, M);
    build(rs, M + 1, r);
    for (int i = 0; i < 26; i++) pushup(u, i);
}
void modify(int u, int l, int r, int k, int c) {
    if (l <= t[c][u].l && r >= t[c][u].r) {
        maketag(u, k, c);
        return ;
    }
    pushdown(u, c);
    int M = (t[c][u].l + t[c][u].r) >> 1;
    if (l <= M) modify(ls, l, r, k, c);
    if (r > M) modify(rs, l, r, k, c);
    pushup(u, c);
}
int query(int u, int l, int r, int c) {
    if (l <= t[c][u].l && r >= t[c][u].r) return t[c][u].val;
    pushdown(u, c);
    int M = (t[c][u].l + t[c][u].r) >> 1, res = 0;
    if (l <= M) res += query(ls, l, r, c);
    if (r > M) res += query(rs, l, r, c);
    return res;
}
void print(int u) {
    if (t[0][u].l == t[0][u].r) {
        for (int i = 0; i < 26; i++) {
            if (t[i][u].val) {
                printf("%c", i + 'a');
                return ;
            }
        }
    }
    for (int i = 0; i < 26; i++) {
        pushdown(u, i);
    }
    print(ls);
    print(rs);
}
void solve(int l, int r, int i, int &p) {
    int cnt = query(1, l, r, i);
    if (!cnt) return ;
    modify(1, l, r, 0, i);
    modify(1, p, p + cnt - 1, 1, i);
    p += cnt;
}

int main() {
    //freopen("1.in", "r", stdin);
    //freopen("1.out", "w", stdout);
    scanf("%d%d", &n, &m);
    scanf("%s", a + 1);
    build(1, 1, n);
    while (m--) {
        int op, l, r;
        scanf("%d%d%d", &l, &r, &op);
        int p = l;
        if (op == 1) {
            for (int i = 0; i < 26; i++) {
                solve(l, r, i, p);
            }
        } else {
            for (int i = 25; i >= 0; i--) {
                solve(l, r, i, p);
            }
        }
    }
    print(1);
    return 0;
}

F. 1.树上操作

设节点 u 的 DFS 序为 dfnu,子树大小为 szu

重要结论:

u 的子树中每个节点的 DFS 序是一段连续区间,为 [dfnu,dfnu+szu1]

证明显然,略。

那么我们就将树上的问题转为区修区查问题,线段树维护即可。

代码似乎是复制的树链剖分模板然后改的,凑合着看吧。

Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;

const int N = 1e6 + 10;
typedef long long ll;

struct node{
	ll val;
	int l, r; 
	int lzy;
}t[N << 2];
struct edge{
	int to, nxt;
}e[N * 2];

int n, m, r, p, vt, tot;
int a[N], fa[N], h[N], wc[N], sz[N];
int top[N], dfn[N], rdfn[N], dep[N];

//Graph
void add(int u, int v) {
	e[tot].to = v;
	e[tot].nxt = h[u];
	h[u] = tot++;
}

//Quick Read
inline int rd() {
    int s = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9') {
		if (ch == '-') f = -1; 
		ch = getchar();
	}
    while ('0' <= ch && ch <= '9') {
		s = (s << 3) + (s << 1) + ch - 48; 
		ch = getchar();
	}
    return s * f;
}

//Segment Tree
inline void pushup(const int u) {
	t[u].val = t[ls].val + t[rs].val;
}
inline void maketag(const int u, int x) {
	t[u].lzy += x;
	t[u].val += x * (t[u].r - t[u].l + 1);
}
inline void pushdown(const int u) {
    maketag(ls, t[u].lzy);
    maketag(rs, t[u].lzy);
    t[u].lzy = 0;
}
void build(const int u, int l, int r) {
	t[u].l = l, t[u].r = r, t[u].lzy = 0;
	if (l == r) {
		t[u].val = a[rdfn[l]];
		return ;
	}
	int M = (l + r) >> 1;
	build(ls, l, M);
	build(rs, M + 1, r);
	pushup(u);
}
void modify(const int u, int l, int r, int k) {
	if (l > r) swap(l, r);
    if (l <= t[u].l && r >= t[u].r) maketag(u, k);
    else {
        pushdown(u);
        int M = (t[u].l + t[u].r) >> 1;
        if (l <= M) modify(ls, l, r, k);
        if (r > M) modify(rs, l, r, k);
        pushup(u);
    }
}
int query(const int u, int l, int r) {
	if (l > r) swap(l, r);
    if (l <= t[u].l && r >= t[u].r) return t[u].val;
    pushdown(u);
    int M = (t[u].l + t[u].r) >> 1, sum = 0;
    if (l <= M) sum += query(ls, l, r);
    if (r > M) sum += query(rs, l, r);
    return sum; 
}

//Deep First Search
void dfs1(int u, int fat) {
	fa[u] = fat;
	sz[u] = 1;
	dep[u] = dep[fat] + 1;
	for (int i = h[u]; i != -1; i = e[i].nxt) {
		if (e[i].to == fat) continue;
		int v = e[i].to;
		dfs1(v, u);
		sz[u] += sz[v];
		if (sz[u] > sz[wc[u]]) wc[u] = v;
	}
}
void dfs2(int u, int tp) {
	dfn[u] = ++vt;
	rdfn[vt] = u;
	top[u] = tp;
	if (wc[u]) dfs2(wc[u], tp);
	for (int i = h[u]; i != -1; i = e[i].nxt) {
		int v = e[i].to;
		if (v == fa[u] || v == wc[u]) continue ;
		dfs2(v, v);
	}
}

//Tree Chain Partitioning
void modify_son(int x, int k) {
	modify(1, dfn[x], dfn[x] + sz[x] - 1, k);
}
int query_son(int x) {
	ll ans = 0;
	ans = query(1, dfn[x], dfn[x] + sz[x] - 1);
	return ans;
}

signed main() {
    memset(h, -1, sizeof h);
    tot = 0;
	n = rd(), m = rd(), r = rd();
	for (int i = 1; i <= n; ++i) a[i] = rd();
	for (int i = 1; i < n; ++i) {
		int u, v;
		u = rd(), v = rd();
		add(u, v);
		add(v, u);
	}
	dfs1(r, 0);
	dfs2(r, r);
	build(1, 1, n);
	while (m--) {
		int op, x, y, u, k;
		op = rd();
		if (op == 1) {
			x = rd(), k = rd();
			modify_son(x, k);
		} else {
			x = rd();
            printf("%lld\n", query_son(x));
		} 
	}
	return 0;
}

G. 2.取模问题

维护一个区间最大值和一个区间和。取模时如果发现区间最大值小于模数就直接返回,否则暴力递归取模。由于 xmodp<x2,p<x,所以这个递归取模最多取 log 次。

Code
#include <iostream>
#include <cstdio>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;

const int N = 1e5 + 10;
int n, q, a[N];

struct node{
	int val;
	int l, r; 
	int mx;
}t[N << 2];

inline int rd() {
    int s = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9') {
		if (ch == '-') f = -1; 
		ch = getchar();
	}
    while ('0' <= ch && ch <= '9') {
		s = (s << 3) + (s << 1) + ch - 48; 
		ch = getchar();
	}
    return s * f;
}
inline void pushup(const int u) {
	t[u].val = t[ls].val + t[rs].val;
	t[u].mx = max(t[ls].mx, t[rs].mx);
}
void build(const int u, int l, int r) {
	t[u].l = l, t[u].r = r;
	if (l == r) {
		t[u].val = t[u].mx = a[l];
		return ;
	}
	int M = (l + r) >> 1;
	build(ls, l, M);
	build(rs, M + 1, r);
	pushup(u);
}
int query(const int u, int l, int r) {
    if (l <= t[u].l && r >= t[u].r) return t[u].val;
    int M = (t[u].l + t[u].r) >> 1, sum = 0;
    if (l <= M) sum += query(ls, l, r);
    if (r > M) sum += query(rs, l, r);
    return sum; 
}
void modify(const int u, int l, int r, int k) {
    if (t[u].l == t[u].r) {
    	t[u].val = t[u].mx = k;
    	return;
	}
    int M = (t[u].l + t[u].r) >> 1;
    if (l <= M) modify(ls, l, r, k);
    if (r > M) modify(rs, l, r, k);
    pushup(u);
}
void mmod(const int u, int l, int r, int k) {
	if (t[u].mx < k) return;
    if (t[u].l == t[u].r) {
    	t[u].val %= k;
		t[u].mx %= k;
		return;
	}
    int M = (t[u].l + t[u].r) >> 1;
    if (l <= M) mmod(ls, l, r, k);
    if (r > M) mmod(rs, l, r, k);
    pushup(u);
}

signed main() {
	cin >> n >> q;
	for (int i = 1; i <= n; i++) a[i] = rd();
	build(1, 1, n);
	while (q--) {
		int op = rd(), x = rd(), y = rd();
		if (op == 1) {
			printf("%lld\n", query(1, x, y));
		} else if (op == 2) {
			int k = rd();
			mmod(1, x, y, k);
		} else {
			modify(1, x, x, y);
		}
	}
    return 0;
}

H. 3.魔法传输

线段树维护差分数组,区间修改时将 [l,r] 加一,r+1rl+1。查询时查询区间 [1,x] 即可。

Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;

const int N = 1e5 + 10;
int n, q, a[N];

struct node{
	int val;	
	int l, r;
	int lzy;
}t[N << 2];

void pushup(int u) {
	t[u].val = t[ls].val + t[rs].val;
}
void maketag(int u, int x) {
	t[u].lzy += x;
	t[u].val += (t[u].r - t[u].l + 1) * x;
}
void pushdown(int u) {
	if (t[u].lzy) {
		maketag(ls, t[u].lzy);
		maketag(rs, t[u].lzy);
		t[u].lzy = 0;
	}
}
void build(int u, int l, int r) {
	t[u].val = t[u].lzy = 0;
	t[u].l = l, t[u].r = r;
	if (l == r) {
		t[u].val = a[l];
		return ;
	}
	int M = (l + r) >> 1;
	build(ls, l, M);
	build(rs, M + 1, r);
	pushup(u);
}
void modify(int u, int l, int r, int k) {
	if (l <= t[u].l && t[u].r <= r) maketag(u, k);
	else {
		pushdown(u);
		int M = (t[u].l + t[u].r) >> 1;
		if (l <= M) modify(ls, l, r, k);
		if (r > M) modify(rs, l, r, k);
		pushup(u);
	}
}
int query(int u, int l, int r) {
	if (l <= t[u].l && t[u].r <= r) return t[u].val;
	pushdown(u);
	int M = (t[u].l + t[u].r) >> 1, res = 0;
	if (l <= M) res += query(ls, l, r);
	if (r > M) res += query(rs, l, r);
	return res;
}

signed main() {
	scanf("%lld%lld", &n, &q);
	build(1, 1, n + 1);
	while (q--) {
		char op[3];
		scanf("%s", op);
		if (op[0] == 'C') {
            int l, r;
			scanf("%lld%lld", &l, &r);
			modify(1, l, r, 1);
            modify(1, r + 1, r + 1, l - r - 1);
		} else {
            int x;
            scanf("%lld", &x);
			printf("%lld\n", query(1, 1, x));
		}
	}
	return 0;
}

I. 4.和或异或

打一个标记,记录线段树这一层取或还是异或,然后合并(pushup)时将标记取反即可。

Code
#include <bits/stdc++.h>
#define int long long 
#define ls u << 1
#define rs u << 1 | 1
using namespace std;

const int N = 1e6 + 10;
int n, q, a[N];

struct tree{
    int l, r;
    int fl, val;
}t[N << 2];

void pushup(int u) {
    t[u].fl = t[ls].fl ^ 1;
    if (t[u].fl == 1) t[u].val = t[ls].val | t[rs].val;
    else t[u].val = t[ls].val ^ t[rs].val;
}
void build(int u, int l, int r) {
    t[u].l = l, t[u].r = r;
    if (l == r) {
        t[u].val = a[l];
        return ;
    }
    int M = (l + r) >> 1;
    build(ls, l, M);
    build(rs, M + 1, r);
    pushup(u);
}
void modify(int u, int p, int k) {
    if (t[u].l == t[u].r) {
        t[u].val = k;
        return ;
    }
    int M = (t[u].l + t[u].r) >> 1;
    if (p <= M) modify(ls, p, k);
    else modify(rs, p, k);
    pushup(u);
}

signed main() {
    scanf("%lld%lld", &n, &q);
    for (int i = 1; i <= (1 << n); i++) {
        scanf("%lld", &a[i]);
    }
    build(1, 1, (1 << n));
    while (q--) {
        int p, x;
        scanf("%lld%lld", &p, &x);
        modify(1, p, x);
        printf("%lld\n", t[1].val);
    }
    return 0;
}

J. 5.括号匹配

记录一个区间内匹配括号数量、未匹配的左括号数量、未匹配的右括号数量。

合并(pushup)方式与 D 题类似,很简单。请读者对照代码自行理解。

Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
using namespace std;

const int N = 1e6 + 10;
int n, m;
char ch[N];

struct tree{
    int l, r;
    int la, ra, val;
}t[N << 2], k;

void pushup(int u) {
    int tp = min(t[ls].la, t[rs].ra);
    t[u].val = t[ls].val + t[rs].val + tp;
    t[u].la = t[ls].la + t[rs].la - tp;
    t[u].ra = t[ls].ra + t[rs].ra - tp;
}
void build(int u, int l, int r) {
    t[u].l = l, t[u].r = r;
    if (l == r) {
        if (ch[l] == '(') t[u].la++;
        if (ch[l] == ')') t[u].ra++;
        return ;
    }
    int M = (l + r) >> 1;
    build(ls, l, M);
    build(rs, M + 1, r);
    pushup(u);
}
void query(int u, int l, int r) {
    if (l <= t[u].l && t[u].r <= r) {
        int tp = min(k.la, t[u].ra);
        k.val += (t[u].val + tp);
        k.la += (t[u].la - tp);
        k.ra += (t[u].ra - tp);
        return ;
    }
    int M = (t[u].l + t[u].r) >> 1;
    if (l <= M) query(ls, l, r);
    if (r > M) query(rs, l, r);
    pushup(u);
}

int main() {
    cin >> n >> m;
    cin >> ch + 1;
    build(1, 1, n);
    while (m--) {
        int l, r;
        scanf("%d%d", &l, &r);
        k = {0, 0, 0, 0, 0};
        query(1, l, r);
        printf("%d\n", k.val * 2);
    }
    return 0;
}
posted @   Eliauk_FP  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示