线段树

思路

将区间二分,叶子结点保存的就是一个信息,而父亲节点则是保存两个儿子节点的信息的集合,例如和,积,个数,种类的种种信息。需要建树,维护时不仅需要修改当前节点,还需要将所维护的信息向上传递,修改上层区间时也需要向下传递。

显然的是,从下到上的传递方式时间复杂度是 \(O(\log n)\) 的,因为只需要走一条链,而从上到下则是 \(O(n)\) 的,则违背了我们的初心。于是有了懒标记记录下,查询时再修改。

查询与修改时,都用递归从根节点向下到待修改区间跑一遍,然后待所修改区间包含当前区间时就修改即可。然后修改完后向上传递是就顺带从下到上传递信息了。

例题一:线段树1

思路

线段树最基本的板子了,只需要维护区间和即可,\(pushup(x):tree[x].sum=tree[x<<1].sum+tree[x<<1|1].sum\)

\(Code\)

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n, m;
int x, y, k, op;
struct SegmentTree{
	int l, r, sum, lazy;
}tree[400005];
int a[100005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	} 
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
}

inline void pushdown(int u){
	if(tree[u].lazy){
		tree[u<<1].lazy += tree[u].lazy;
		tree[u<<1|1].lazy += tree[u].lazy;
		tree[u<<1].sum += (tree[u<<1].r-tree[u<<1].l+1) * tree[u].lazy;
		tree[u<<1|1].sum += (tree[u<<1|1].r-tree[u<<1|1].l+1) * tree[u].lazy;
		tree[u].lazy = 0;
	}
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].sum = a[l];
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void add(int u, int l, int r, int num){
	if(l <= tree[u].l && r >= tree[u].r){
		tree[u].lazy += num;
		tree[u].sum += num*(tree[u].r-tree[u].l+1);
		return;
	}
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	if(l <= mid)	add(u<<1, l, r, num);
	if(r > mid)	add(u<<1|1, l, r, num);
	pushup(u);
}

inline int search(int u, int l, int r){
	if(l <= tree[u].l && r >= tree[u].r)	return tree[u].sum;
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1, ans = 0;
	if(l <= mid)	ans += search(u<<1, l, r);
	if(r > mid)	ans += search(u<<1|1, l, r);
	return ans;
}

signed main(){
	read(n), read(m);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		read(op);
		if(op == 1){
			read(x), read(y), read(k);
			add(1, x, y, k);
		}
		if(op == 2){
			read(x), read(y);
			write(search(1, x, y));
			putchar('\n');
		}
	}
	return 0;
}

例题二:线段树2

思路

类似于线段树1的思路,只不过要维护加和乘两种操作。思考一下可以发现先维护乘法,加法的 \(tag\) 乘上所乘的数即可。别的和线段树1都一模一样。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m, mod;
int a[100005];
int op, x, y, k;
struct SegmentTree{
	int l, r;
	long long sum, plz, mlz;
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 1;
	while(isdigit(q))	x = (x<<1) + (x<<3) + (q^48), q = getchar();
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0)	putchar('-'), x = -x;
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].sum = (tree[u<<1].sum+tree[u<<1|1].sum) % mod;
}

inline void pushdown(int u){
	tree[u<<1].sum = (tree[u<<1].sum * tree[u].mlz + tree[u].plz*(tree[u<<1].r-tree[u<<1].l+1)) % mod;
	tree[u<<1|1].sum = (tree[u<<1|1].sum * tree[u].mlz + tree[u].plz*(tree[u<<1|1].r-tree[u<<1|1].l+1)) % mod;
	tree[u<<1].mlz = (tree[u<<1].mlz * tree[u].mlz) % mod;
	tree[u<<1|1].mlz = (tree[u<<1|1].mlz * tree[u].mlz) % mod;
	tree[u<<1].plz = (tree[u<<1].plz*tree[u].mlz + tree[u].plz) % mod;
	tree[u<<1|1].plz = (tree[u<<1|1].plz*tree[u].mlz + tree[u].plz) % mod;
	tree[u].plz = 0, tree[u].mlz = 1;
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r, tree[u].mlz = 1;
	if(l == r){
		tree[u].sum = a[l];
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void mul(int u, int l, int r, int k){
	if(tree[u].l >= l && tree[u].r <= r){
		tree[u].sum = (tree[u].sum * k) % mod;
		tree[u].mlz = (tree[u].mlz * k) % mod;
		tree[u].plz = (tree[u].plz * k) % mod;
		return;
	}
	pushdown(u);
	if(tree[u<<1].r >= l)  mul(u<<1, l, r, k);
	if(tree[u<<1|1].l <= r)  mul(u<<1|1, l, r, k);
	pushup(u);
}

inline void add(int u, int l, int r, int k){
	if(tree[u].r < l || tree[u].l > r)  return;
	if(tree[u].l >= l && tree[u].r <= r){
		tree[u].sum += ((tree[u].r - tree[u].l+1) * k) % mod;
		tree[u].plz = (tree[u].plz + k) % mod;
		return;
	}
	pushdown(u);
	if(tree[u<<1].r >= l)	add(u<<1, l, r, k);
	if(tree[u<<1|1].l <= r)	add(u<<1|1, l, r, k);
	pushup(u);
}

inline int search(int u, int l, int r){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].sum;
	pushdown(u);
	int sum = 0;
	if(tree[u<<1].r >= l)  sum += search(u<<1, l, r) % mod;
	if(tree[u<<1|1].l <= r)  sum += search(u<<1|1, l, r) % mod;
	return sum % mod;
}

int main(){
	read(n), read(m), read(mod);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		read(op);
		if(op == 1){
			read(x), read(y), read(k);
			mul(1, x, y, k);
		}
		if(op == 2){
			read(x), read(y), read(k);
			add(1, x, y, k);
		}
		if(op == 3){
			read(x), read(y);
			write(search(1, x, y));
			putchar('\n');
		}
	}
	return 0;
}

例题三:扫描线

思路

很经典的一道题目,求面积并。很多线段树问题都可以转化为这个问题。

面积并的问题很难用其他的方法瞎搞,容斥原理之类的作法显然是不可行的,因为容斥的枚举时间复杂度是 \(O(n!)\) 的。

考虑从边开始思考,于是将所有的横边记录下来,从下到上的进行排列,将下边权值赋为 \(1\),上边权值赋为 \(-1\)。把每一个端点对应的 \(x\) 轴坐标离散化以后,相邻两点之间的线段作为建树的叶节点。每个节点维护两个信息 \(sum\) 表示当前被覆盖次数,\(len\) 表示区间长度,上传时若 \(sum\),则 \(len=x_{r+1}-x_{l}\),否则则为 \({tree_{lson}}_{len}+{tree_{rson}}_{len}\)。每次统计两条边之间的距离乘当前线段长度 \(({line_{i+1}}_h-{line_{i}}_h)\times {tree_1}_{len}\) 即可。

\(Code\)

#include<bits/stdc++.h>
#define y1 y_1
#define y2 y_2
#define ll long long
using namespace std;

int n;
long long x1, x2, y1, y2, x[2000005];
long long ans = 0;

struct Line{
	long long l, r, h;
	int flag;
}line[2000005];

struct SegmentTree{
	int l, r, sum;
	long long len;
}tree[8000005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline bool cmp(Line a, Line b){
	return a.h < b.h;
}

inline void pushup(int u){
	int l = tree[u].l, r = tree[u].r;
	if(tree[u].sum)	tree[u].len = x[r+1]-x[l];
	else	tree[u].len = tree[u<<1].len+tree[u<<1|1].len;
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r)	return;
	int mid = (l+r) >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	return;
}


inline void modify(int u, long long l, long long r, int c){
	if(x[tree[u].r+1] <= l || r <= x[tree[u].l])	return;
	if(l <= x[tree[u].l] && x[tree[u].r+1] <= r){
		tree[u].sum += c;
		pushup(u);
		return;
	}
	modify(u<<1, l, r, c);
	modify(u<<1|1, l, r, c);
	pushup(u);
}

int main(){
	read(n); 
	for(register int i = 1; i <= n; ++i){
		read(x1), read(y1), read(x2), read(y2);
		x[(i<<1)-1] = x1, x[i<<1] = x2;
		line[(i<<1)-1] = (Line){x1, x2, y1, 1};
		line[i<<1] = (Line){x1, x2, y2, -1};
	}
	n <<= 1;
	sort(line+1, line+n+1, cmp);
	sort(x+1, x+n+1);
	int tot = unique(x+1, x+n+1)-x-1;
	build(1, 1, tot-1);
	long long ans = 0;
	for(register int i = 1; i < n; ++i){
		modify(1, line[i].l, line[i].r, line[i].flag);
		ans += tree[1].len * (line[i+1].h-line[i].h);
	}
	write(ans);
	return 0;
}

例题四:数学计算

思路

奇思妙想线段树。

把操作变为线段树的叶节点,把每个节点初始值赋为 \(1\),若为操作 \(1\) 则修改否则又改为 \(1\)

\(Code\)

#include<bits/stdc++.h>
#define int long long
using namespace std;

int t, mod, q, op, x, num, tree[400005];

template<typename T>
inline void read(T&x){
    x = 0; char q; bool f = 1;
    while(!isdigit(q = getchar()))  if(q == '-')    f = 0;
    while(isdigit(q)){
        x = (x<<1) + (x<<3) + (q^48);
        q = getchar();
    }
    x = f?x:-x;
}

template<typename T>
inline void write(T x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)   write(x/10);
    putchar(x%10+'0');
}

void build(int u, int l, int r){
	tree[u] = 1;
	if(l == r)	return;
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
}

void modify(int u, int l, int r, int x, int val){
	if(l == r){
		if(!val)	tree[u] = 1;
		else	tree[u] = val;
		return;
	}
	int mid = l+r >> 1;
	if(x <= mid)	modify(u<<1, l, mid, x, val);
	else	modify(u<<1|1, mid+1, r, x, val);
	tree[u] = (tree[u<<1] * tree[u<<1|1]) % mod;
}

signed main(){
	read(t);
	while(t--){
		read(q), read(mod);
		build(1, 1, q);
		for(register int i = 1; i <= q; ++i){
			read(op), read(x);
			if(op == 1){
				modify(1, 1, q, i, x);
				write(tree[1]%mod);
				putchar('\n');
			}
			else{
				modify(1, 1, q, x, 0);
				write(tree[1]%mod);
				putchar('\n');
			}
		}
	}
	return 0;
}

例题五:楼房重建

思路

很容易想到维护斜率,因此直接线段树维护即可,比较难操作的是 \(pushup\),左区间一定是已知的,因此只需要递归右区间与左区间最大值之间的关系即可。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
double a[100005];
int x, y;

struct SegmentTree{
	double sl;
	int ans;
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	} 
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup1(int u){
	tree[u].sl = max(tree[u<<1].sl, tree[u<<1|1].sl);
}

inline int pushup2(int u, double sl, int l, int r){
	if(tree[u].sl <= sl)	return 0;
	if(a[l] > sl) return tree[u].ans;
	if(l == r)	return a[l] > sl;
	int mid = l+r >>1;
	if(tree[u<<1].sl <= sl)	return pushup2(u<<1|1, sl, mid+1, r);
	else	return	pushup2(u<<1, sl, l, mid)+tree[u].ans-tree[u<<1].ans;
}

inline void modify(int u, int l, int r, int pos, int num){
	if(l == r && l == pos){
		tree[u].sl = (double)num/pos;
		tree[u].ans = 1;
		return;
	}
	int mid = l+r >> 1;
	if(pos <= mid)	modify(u<<1, l, mid, pos, num);
	else	if(pos > mid)	modify(u<<1|1, mid+1, r, pos, num);
	pushup1(u);
	tree[u].ans = tree[u<<1].ans+pushup2(u<<1|1, tree[u<<1].sl, mid+1, r);
}

int main(){
	read(n), read(m);
	for(register int i = 1; i <= m; ++i){
		read(x), read(y);
		a[x] = (double)y/x;
		modify(1, 1, n, x, y);
		write(tree[1].ans);
		puts("");
	}
	return 0;
}

例题六:XOR 的艺术

思路

线段树版题。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, q;
string s;
bool a[200005];
bool op;
int x, y;

struct SegmentTree{
	int l, r, num;
	bool swap;
}tree[800005];

template<typename T>
inline void read(T&x){
    x = 0; char q; bool f = 1;
    while(!isdigit(q = getchar()))  if(q == '-')    f = 0;
    while(isdigit(q)){
        x = (x<<1) + (x<<3) + (q^48);
        q = getchar();
    }
    x = f?x:-x;
}

template<typename T>
inline void write(T x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)   write(x/10);
    putchar(x%10+'0');
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].num = a[l];
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	tree[u].num = tree[u<<1].num+tree[u<<1|1].num;
}

inline void pushdown(int u){
	tree[u].swap = 0;
	tree[u<<1].swap ^= 1, tree[u<<1|1].swap ^= 1;
	tree[u<<1].num = tree[u<<1].r-tree[u<<1].l+1-tree[u<<1].num;
	tree[u<<1|1].num = tree[u<<1|1].r-tree[u<<1|1].l+1-tree[u<<1|1].num;
}

inline void change(int u, int l, int r){
	if(l <= tree[u].l && r >= tree[u].r){
		tree[u].swap ^= 1;
		tree[u].num = tree[u].r-tree[u].l+1-tree[u].num;
		return;
	}
	if(tree[u].swap)	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	if(l <= mid)	change(u<<1, l, r);
	if(r > mid)	change(u<<1|1, l, r);
	tree[u].num = tree[u<<1].num+tree[u<<1|1].num;
}

inline int search(int u, int l, int r){
	if(l <= tree[u].l && r >= tree[u].r)	return tree[u].num;
	if(tree[u].swap)	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	int ans = 0;
	if(l <= mid)	ans += search(u<<1, l, r);
	if(r > mid)	ans += search(u<<1|1, l, r);
	return ans;
}

int main(){
	read(n), read(q);
	cin >> s;
	s = ' ' + s;
	for(register int i = 1; i <= n; ++i){
		if(s[i] == '0')	a[i] = 0;
		else	a[i] = 1;
	}
	build(1, 1, n);
	for(register int i = 1; i <= q; ++i){
		read(op), read(x), read(y);
		if(op == 0)	change(1, x, y);
		else{
			write(search(1, x, y));
			putchar('\n');
		}
	}
	return 0;
} 

例题七:窗口的星星

思路

一个高级版本的扫描线。

如果把星星的贡献范围看作一个矩形的话,那么问题就转化为了平面上有若干个矩形,每个矩形有一个权值,问哪一个坐标上矩形的权值最大。

于是问题就变为了扫描线求最大值的问题,注意处理一下边框上的点就可以了,打一个 \(lazy\) 的下放标记就可以了。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int t, n, w, h;
long long x, y, l;
long long a[20005];
long long ans = 0;

struct Line{
	long long l, r, h, val;
}line[20005];

struct SegmentTree{
	int l, r;
	long long maxn, lazy;
}tree[80005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline bool cmp(Line a, Line b){
	if(a.h != b.h)	return a.h < b.h;
	return a.val > b.val;
}

inline void pushup(int u){
	tree[u].maxn = max(tree[u<<1].maxn, tree[u<<1|1].maxn);
}

inline void pushdown(int u){
	tree[u<<1].lazy += tree[u].lazy, tree[u<<1|1].lazy += tree[u].lazy;
	tree[u<<1].maxn += tree[u].lazy, tree[u<<1|1].maxn += tree[u].lazy;
	tree[u].lazy = 0;
	return;
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r)	return;
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	return;
}

inline void modify(int u, int l, int r, int num){
	if(tree[u].l >= l && tree[u].r <= r){
		tree[u].lazy += num;
		tree[u].maxn += num;
		return;
	}
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	modify(u<<1, l, r, num);
	if(mid < r)	modify(u<<1|1, l, r, num);
	pushup(u);
}

int main(){
	read(t);
	while(t--){
		memset(tree, 0, sizeof(tree));
		memset(line, 0, sizeof(line));
		read(n), read(w), read(h);
		for(register int i = 1; i <= n; ++i){
			read(x), read(y), read(l);
			a[(i<<1)-1] = y, a[i<<1] = y+h-1;
			line[(i<<1)-1] = (Line){y, y+h-1, x, l};
			line[i<<1] = (Line){y, y+h-1, x+w-1, -l};
		}
		n <<= 1;
		sort(line+1, line+n+1, cmp);
		sort(a+1, a+n+1);
		int tot = unique(a+1, a+n+1)-a-1;
		for(register int i = 1; i <= n; ++i){
			int pos1 = lower_bound(a+1, a+tot+1, line[i].l)-a;
			int pos2 = lower_bound(a+1, a+tot+1, line[i].r)-a;
			line[i].l = pos1, line[i].r = pos2;
		}
		build(1, 1, tot);
		long long ans = 0;
		for(register int i = 1; i <= n; ++i){
			modify(1, line[i].l, line[i].r, line[i].val);
			ans = max(ans, tree[1].maxn);
		}
		write(ans);
		puts("");
	}
	return 0;
}

例题八:维护序列

思路

线段树二的单点修改版。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

long long n, m, mod;
long long a[100005];
long long op, x, y, z;
struct node{
    int l, r;
    long long sum, mlz, plz;
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].sum = (tree[u<<1].sum + tree[u<<1|1].sum)%mod;
}

inline void pushdown(int u){
	long long k1 = tree[u].mlz, k2 = tree[u].plz;
	tree[u<<1].sum = (tree[u<<1].sum*k1+k2*(tree[u<<1].r-tree[u<<1].l+1))%mod, tree[u<<1|1].sum = (tree[u*2+1].sum*k1+k2*(tree[u*2+1].r-tree[u*2+1].l+1))%mod;
	tree[u<<1].mlz = (tree[u*2].mlz*k1)%mod, tree[u<<1|1].mlz = (tree[u*2+1].mlz*k1)%mod;
	tree[u<<1].plz = (tree[u*2].plz*k1+k2)%mod, tree[u<<1|1].plz = (tree[u*2+1].plz*k1+k2)%mod;
	tree[u].plz = 0, tree[u].mlz = 1;
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r, tree[u].mlz = 1;
	if(l == r){
		tree[u].sum = a[l]%mod;
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void mul(int u, int l, int r, long long k){
	if(tree[u].l >= l && tree[u].r <= r){
		tree[u].sum = (tree[u].sum * k) % mod;
		tree[u].mlz = (tree[u].mlz * k) % mod;
		tree[u].plz = (tree[u].plz * k) % mod;
		return;
	}
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	mul(u<<1, l, r, k);
	if(mid < r)	mul(u<<1|1, l, r, k);
	pushup(u);
}

inline void add(int u, int l, int r, long long k){
	if(tree[u].l >= l && tree[u].r <= r){
		tree[u].sum += ((tree[u].r - tree[u].l+1) * k) % mod;
		tree[u].plz = (tree[u].plz + k) % mod;
		return;
	}
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	add(u<<1, l, r, k);
	if(mid < r)	add(u<<1|1, l, r, k);
	pushup(u);
}

inline long long search(int u, int l, int r){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].sum;
	pushdown(u);
	long long sum = 0;
	int mid = tree[u].l+tree[u].r>>1;
	if(mid >= l)	sum += search(u<<1, l, r)%mod;
	if(mid < r)	sum += search(u<<1|1, l, r)%mod;
	return sum%mod;
}

int main(){
	read(n), read(mod);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	read(m);
	for(register int i = 1; i <= m; ++i){
		read(op);
		if(op == 1){
			read(x), read(y), read(z);
			z %= mod;
			mul(1, x, y, z);
		}
		if(op == 2){
			read(x), read(y), read(z);
			z %= mod;
			add(1, x, y, z);
		}
		if(op == 3){
			read(x), read(y);
			write(search(1, x, y)), puts("");
		}
	}
	return 0;
}

例题九:I Hate It

思路

奇怪的错误卡掉了?

单点修改,区间最大值,无需 \(pushdown\)

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
int a[2000005];
char op;
int x, y;
struct node{
    int l, r, maxn;
}tree[8000005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].maxn = max(tree[u<<1].maxn, tree[u<<1|1].maxn);
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].maxn = a[l];
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void modify(int u, int pos, int num){
	if(tree[u].l == tree[u].r){
		tree[u].maxn = max(tree[u].maxn, num);
		return;
	}
	int mid = tree[u].l+tree[u].r >> 1;
	if(pos <= mid)	modify(u<<1, x, num);
	else	modify(u<<1|1, x, num);
	pushup(u);
}

inline int search(int u, int l, int r){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].maxn;
	int maxn = 0;
	int mid = tree[u].l+tree[u].r>>1;
	if(mid >= l)	maxn = max(maxn, search(u<<1, l, r));
	if(mid < r)	maxn = max(maxn, search(u<<1|1, l, r));
	return maxn;
}

int main(){
	read(n), read(m);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		cin >> op;
		read(x), read(y);
		if(op == 'Q'){
			write(search(1, x, y));
			puts("");
		}
		else	modify(1, x, y);
	}
	return 0;
}

例题十:XOR on Segment

思路

异或操作和加法操作显然时不能共同使用的,于是考虑如何将按位异或转变为一个正常的操作——暴力拆掉每一位,每一位分别维护,如果修改就取反打上标记即可,然后时间复杂度变为 \(O(\log a_in\log n)\) 即可,有一个高级的名字,拆位线段树。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m, a[100005];
int op, x, y, z;
struct SegmentTree{
	int l, r;
	long long lazy, num[25];
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	for(register int i = 1; i <= 20; ++i)	tree[u].num[i] = tree[u<<1].num[i]+tree[u<<1|1].num[i];
}

inline void pushdown(int u){
	int mid = tree[u].l+tree[u].r >> 1;
	tree[u<<1].lazy ^= tree[u].lazy, tree[u<<1|1].lazy ^= tree[u].lazy;
	for(register int i = 1; i <= 20; ++i){
		if(tree[u].lazy&1)	tree[u<<1].num[i] = (mid-tree[u].l+1)-tree[u<<1].num[i], tree[u<<1|1].num[i] = (tree[u].r-mid)-tree[u<<1|1].num[i];
		tree[u].lazy >>= 1;
	}
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		for(register int i = 1; i <= 20; ++i){
			if(a[l]&1)	tree[u].num[i] = 1;
			a[l] >>= 1;
		}
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void modify(int u, int l, int r, int val){
	if(tree[u].l >= l && tree[u].r <= r){
		tree[u].lazy ^= val;
		for(register int i = 1; i <= 20; ++i){
			if(val&1)	tree[u].num[i] = tree[u].r-tree[u].l+1-tree[u].num[i]; 
			val >>= 1;
		}
		return;
	}
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	modify(u<<1, l, r, val);
	if(mid < r)	modify(u<<1|1, l, r, val);
	pushup(u);
}

inline long long search(int u, int l, int r){
	if(tree[u].l >= l && tree[u].r <= r){
		long long Pow = 1, ans = 0;
		for(register int i = 1; i <= 20; ++i){
			ans += Pow*tree[u].num[i];
			Pow <<= 1;
		}
		return ans;
	}
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	long long ans = 0;
	if(mid >= l)	ans += search(u<<1, l, r);
	if(mid < r)	ans += search(u<<1|1, l, r);
	return ans;
}

int main(){
	read(n);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	read(m);
	for(register int i = 1; i <= m; ++i){
		read(op);
		if(op == 1){
			read(x), read(y);
			write(search(1, x, y));
			puts("");
		}
		if(op == 2){
			read(x), read(y), read(z);
			modify(1, x, y, z);		}
	}
	return 0;

例题十一:STEP

思路

如果把 \(L,R\) 视作 \(0,1\),那么问题就是最长的形如 \(...01010101...\) 串的长度是多少。

我们设 \(maxn\) 是区间最大值,\(ansl\) 是从左端点开始的最大值,\(ansr\) 是从右端点开始的最大值,那么 \(maxn\) 可以分三种情况考虑,左区间最大值对父亲的贡献,右区间最大值对父亲的贡献,左区间右端点与右区间左端点不同时两者之和的贡献,\({tree_u}_{maxn} = \max({tree_{u<<1}}_{maxn},{tree_{u<<1|1}}_{maxn},{tree_{u<<1}}_{ansr}+{tree_{u<<1|1}}_{ansl}(a_{{tree_{u<<1}}_l}\neq a_{{tree_{u<<1|1}}_r}))\)

\(pushup\) 以后就简单了,单点修改,每次输出整个区间的 \(ans\) 即可。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, q;
int x;
struct SegmentTree{
	int l, r;
	int ans, lans, rans;
	bool lnum, rnum;
}tree[800005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].ans = max(tree[u<<1].ans, tree[u<<1|1].ans);
	if(tree[u<<1].rnum ^ tree[u<<1|1].lnum)	tree[u].ans = max(tree[u].ans, tree[u<<1].rans+tree[u<<1|1].lans);
	tree[u].lnum = tree[u<<1].lnum, tree[u].rnum = tree[u<<1|1].rnum;
	if(tree[u<<1].lans == tree[u<<1].r-tree[u<<1].l+1 && tree[u<<1].rnum ^ tree[u<<1|1].lnum)	tree[u].lans = tree[u<<1].lans + tree[u<<1|1].lans;
	else	tree[u].lans = tree[u<<1].lans;
	if(tree[u<<1|1].rans == tree[u<<1|1].r-tree[u<<1|1].l+1 && tree[u<<1].rnum ^ tree[u<<1|1].lnum)	tree[u].rans = tree[u<<1].rans + tree[u<<1|1].rans;
	else	tree[u].rans = tree[u<<1|1].rans;
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].ans = tree[u].lans = tree[u].rans = 1;
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void modify(int u, int pos){
	if(tree[u].l == tree[u].r){
		tree[u].ans = tree[u].lans = tree[u].rans = 1;
		tree[u].lnum = tree[u].rnum = (!tree[u].lnum);
		return;
	}
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= pos)	modify(u<<1, pos);
	else	modify(u<<1|1, pos);
	pushup(u);
}

int main(){
	read(n), read(q);
	build(1, 1, n);
	for(register int i = 1; i <= q; ++i){
		read(x);
		modify(1, x);
		write(tree[1].ans);
		puts("");
	}
	return 0;
}

例题十二:TorCoder

思路

类似于 XOR on Segment 的思路,串通过修改后可以回文当且仅当在串长度为奇数时只有且只有一个字母个数为奇数,当串长度为偶数时对于任意字母个数都为偶。

因此线段树中维护 \(26\) 个值分别表示字母 \(a\) ~ \(z\),每次修改后前后区间修改 \(\frac{1}{2}num_a\) 个即可。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
char c[100005];
int x, y;
int a[100005];
int cnt[30];
struct SegmentTree{
	int l, r;
	int lazy[30], sum[30];
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u, int num){
	tree[u].sum[num] = tree[u<<1].sum[num]+tree[u<<1|1].sum[num];
}

inline void pushdown(int u, int num){
	if(tree[u].lazy[num] == -1)	return;
	tree[u<<1|1].lazy[num] = tree[u<<1].lazy[num] = tree[u].lazy[num];
	tree[u<<1].sum[num] = (tree[u<<1].r-tree[u<<1].l+1)*tree[u<<1].lazy[num];
	tree[u<<1|1].sum[num] = (tree[u<<1|1].r-tree[u<<1|1].l+1)*tree[u<<1|1].lazy[num];
	tree[u].lazy[num] = -1;
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	for(register int i = 1; i <= 26; ++i)	tree[u].lazy[i] = -1;
	if(l == r){
		tree[u].sum[a[l]] = 1;
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	for(register int i = 1; i <= 26; ++i)	pushup(u, i);
}

inline void modify(int u, int l, int r, int num, int val){
	if(tree[u].l >= l && tree[u].r <= r){
		tree[u].lazy[num] = val;
		tree[u].sum[num] = (tree[u].r-tree[u].l+1)*val;
		return;
	}
	pushdown(u, num);
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	modify(u<<1, l, r, num, val);
	if(mid < r)	modify(u<<1|1, l, r, num, val);
	pushup(u, num);
}

inline int query(int u, int l, int r, int num){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].sum[num];
	pushdown(u, num);
	int mid = tree[u].l+tree[u].r >> 1, ans = 0;
	if(mid >= l)	ans += query(u<<1, l, r, num);
	if(mid < r)	ans += query(u<<1|1, l, r, num);
	return ans;
}

inline bool check(int x, int y){
	memset(cnt, 0, sizeof(cnt));
	int tot = 0;
	for(register int i = 1; i <= 26; ++i){
		cnt[i] = query(1, x, y, i);
		if(cnt[i]&1)	tot++;
	}
	if(tot > 1)	return 0;
	return 1;
}

inline void change(int x, int y){
	for(register int i = 1; i <= 26; ++i)	modify(1, x, y, i, 0);
	int l = x, r = y;
	for(register int i = 1; i <= 26; ++i){
		if(cnt[i]&1){
			modify(1, l+r>>1, l+r>>1, i, 1);
			--cnt[i];
		}
		if(!cnt[i])	continue;
		modify(1, l, l+(cnt[i]>>1)-1, i, 1);
		modify(1, r-(cnt[i]>>1)+1, r, i, 1);
		l += (cnt[i]>>1);
		r -= (cnt[i]>>1);
	}
}

int main(){
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
	read(n), read(m);
	scanf("%s", c+1);
	for(register int i = 1; i <= n; ++i)	a[i] = c[i]-'a'+1;
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		read(x), read(y);
		if(check(x, y))	change(x, y);
	}
	for(register int i = 1; i <= n; ++i){
		for(register int j = 1; j <= 26; ++j)
			if(query(1, i, i, j)){
				putchar('a'+j-1);
				break;
			}
	}
	return 0;
}

例题十三:无聊的数列

思路

等差数列加减法,很容易就能想到是维护差分数列了,对于单点求值就是 \(sum_{i=1}^n{a_i}\)。所以区间修改区间求和,线段树版题。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
int a[100005];
int op, x, y, k, d;
struct SegmentTree{
	int l, r;
	long long sum, lazy;
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
}

inline void pushdown(int u){
	if(tree[u].lazy){
		tree[u<<1].sum += (tree[u<<1].r-tree[u<<1].l+1)*tree[u].lazy, tree[u<<1|1].sum += (tree[u<<1|1].r-tree[u<<1|1].l+1)*tree[u].lazy;
		tree[u<<1].lazy += tree[u].lazy, tree[u<<1|1].lazy += tree[u].lazy;
		tree[u].lazy = 0;
	}
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r)	return;
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
}

inline void modify(int u, int l, int r, int num){
	if(tree[u].l >= l && tree[u].r <= r){
		tree[u].sum += (tree[u].r-tree[u].l+1)*num;
		tree[u].lazy += num;
		return;
	}
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	modify(u<<1, l, r, num);
	if(mid < r)	modify(u<<1|1, l, r, num);
	pushup(u);
}

inline long long query(int u, int l, int r){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].sum;
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	long long ans = 0;
	if(mid >= l)	ans += query(u<<1, l, r);
	if(mid < r)	ans += query(u<<1|1, l, r);
	return ans;
}

int main(){
	read(n), read(m);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		read(op);
		if(op == 1){
			read(x), read(y), read(k), read(d);
			modify(1, x, x, k);
			if(y > x)	modify(1, x+1, y, d);
			if(y != n)	modify(1, y+1, y+1, -k-(y-x)*d);
		}
		if(op == 2){
			read(x);
			write(query(1, 1, x)+a[x]);
			puts("");
		}
	}
	return 0;
}

例题十四:上帝造题的七分钟 2 / 花神游历各国

思路

有一点离谱的思路,暴力维护一个区间 \(\max\),只有区间 \(\max\) 大于 \(1\) 时才操作。可以证明时间复杂度单次时 \(O(n\log n)\) 的,有效的操作最多操作满 \(39\) 次,暴力维护即可。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
long long a[100005];
int op, x, y;
struct SegmentTree{
	int l, r;
	long long sum, maxn;
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
	tree[u].maxn = max(tree[u<<1].maxn, tree[u<<1|1].maxn);
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].sum = tree[u].maxn = a[l];
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void modify(int u, int l, int r){
	if(tree[u].maxn == 1)	return;
	if(tree[u].l == tree[u].r){
		tree[u].maxn = tree[u].sum = sqrt(tree[u].maxn);
		return;
	}
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	modify(u<<1, l, r);
	if(mid < r)	modify(u<<1|1, l, r);
	pushup(u);
}

inline long long query(int u, int l, int r){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].sum;
	int mid = tree[u].l+tree[u].r >> 1;
	long long ans = 0;
	if(mid >= l)	ans += query(u<<1, l, r);
	if(mid < r)	ans += query(u<<1|1, l, r);
	return ans;
}

int main(){
	read(n);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	read(m);
	for(register int i = 1; i <= m; ++i){
		read(op), read(x), read(y);
		if(x > y)	swap(x, y);
		if(!op)	modify(1, x, y);
		else	write(query(1, x, y)), puts("");
	}
	return 0;
}

例题十五:The Child and Sequence

思路

和上面那道题有点类似,可以证明 \(mod\) 操作只有在大于 \(mod\) 时才能操作,并且一次 \(mod\) 以后就会至少到原数的 \(\frac{1}{2}\),所以时间复杂度只会多带一个 \(\log\)。一样的套路,记录一个区间最大值即可。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
long long a[100005];
int op, x, y, z;
struct SegmentTree{
	int l, r;
	long long sum, maxn;
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
	tree[u].maxn = max(tree[u<<1].maxn, tree[u<<1|1].maxn);
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].sum = tree[u].maxn = a[l];
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void modify(int u, int l, int r, int num){
	if(tree[u].maxn < num)	return;
	if(tree[u].l == tree[u].r){
		tree[u].maxn = tree[u].sum = tree[u].maxn%num;
		return;
	}
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	modify(u<<1, l, r, num);
	if(mid < r)	modify(u<<1|1, l, r, num);
	pushup(u);
}

inline void change(int u, int pos, int num){
	if(tree[u].l == tree[u].r){
		tree[u].maxn = tree[u].sum = num;
		return;
	}
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= pos)	change(u<<1, pos, num);
	else	change(u<<1|1, pos, num);
	pushup(u);
}

inline long long query(int u, int l, int r){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].sum;
	int mid = tree[u].l+tree[u].r >> 1;
	long long ans = 0;
	if(mid >= l)	ans += query(u<<1, l, r);
	if(mid < r)	ans += query(u<<1|1, l, r);
	return ans;
}

int main(){
	read(n), read(m);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		read(op);
		if(op == 1){
			read(x), read(y);
			write(query(1, x, y));
			puts("");
		}
		if(op == 2){
			read(x), read(y), read(z);
			modify(1, x, y, z);
		}
		if(op == 3){
			read(x), read(y);
			change(1, x, y);
		}
	}
	return 0;
}

例题十六:A Simple Task

思路

\(Torcoder\) 比较类似,线段树常见的维护方式。

每个点维护 \(26\) 个数,表示 \(a\)~\(z\) 分别的个数,然后排序就直接修改即可。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
char c[100005];
int x, y, op;
int a[100005];
int cnt[30];
struct SegmentTree{
	int l, r;
	int lazy[30], sum[30];
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u, int num){
	tree[u].sum[num] = tree[u<<1].sum[num]+tree[u<<1|1].sum[num];
}

inline void pushdown(int u, int num){
	if(tree[u].lazy[num] == -1)	return;
	tree[u<<1|1].lazy[num] = tree[u<<1].lazy[num] = tree[u].lazy[num];
	tree[u<<1].sum[num] = (tree[u<<1].r-tree[u<<1].l+1)*tree[u<<1].lazy[num];
	tree[u<<1|1].sum[num] = (tree[u<<1|1].r-tree[u<<1|1].l+1)*tree[u<<1|1].lazy[num];
	tree[u].lazy[num] = -1;
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	for(register int i = 1; i <= 26; ++i)	tree[u].lazy[i] = -1;
	if(l == r){
		tree[u].sum[a[l]] = 1;
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	for(register int i = 1; i <= 26; ++i)	pushup(u, i);
}

inline void modify(int u, int l, int r, int num, int val){
	if(tree[u].l >= l && tree[u].r <= r){
		tree[u].lazy[num] = val;
		tree[u].sum[num] = (tree[u].r-tree[u].l+1)*val;
		return;
	}
	pushdown(u, num);
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	modify(u<<1, l, r, num, val);
	if(mid < r)	modify(u<<1|1, l, r, num, val);
	pushup(u, num);
}

inline int query(int u, int l, int r, int num){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].sum[num];
	pushdown(u, num);
	int mid = tree[u].l+tree[u].r >> 1, ans = 0;
	if(mid >= l)	ans += query(u<<1, l, r, num);
	if(mid < r)	ans += query(u<<1|1, l, r, num);
	return ans;
}

inline bool check(int x, int y){
	memset(cnt, 0, sizeof(cnt));
	int tot = 0;
	for(register int i = 1; i <= 26; ++i){
		cnt[i] = query(1, x, y, i);
		if(cnt[i]&1)	tot++;
	}
	if(tot > 1)	return 0;
	return 1;
}

inline void change1(int x, int y){
	memset(cnt, 0, sizeof(cnt));
	for(register int i = 1; i <= 26; ++i)	cnt[i] = query(1, x, y, i);
	for(register int i = 1; i <= 26; ++i)	modify(1, x, y, i, 0);
	int l = x;
	for(register int i = 1; i <= 26; ++i){
		if(!cnt[i])	continue;
		modify(1, l, l+cnt[i]-1, i, 1);
		l += cnt[i];
	}
}

inline void change2(int x, int y){
	memset(cnt, 0, sizeof(cnt));
	for(register int i = 1; i <= 26; ++i)	cnt[i] = query(1, x, y, i);
	for(register int i = 1; i <= 26; ++i)	modify(1, x, y, i, 0);
	int l = x;
	for(register int i = 26; i >= 1; --i){
		if(!cnt[i])	continue;
		modify(1, l, l+cnt[i]-1, i, 1);
		l += cnt[i];
	}
}

int main(){
	read(n), read(m);
	scanf("%s", c+1);
	for(register int i = 1; i <= n; ++i)	a[i] = c[i]-'a'+1;
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		read(x), read(y), read(op);
		if(op)	change1(x, y);
		else	change2(x, y);
	}
	for(register int i = 1; i <= n; ++i){
		for(register int j = 1; j <= 26; ++j)
			if(query(1, i, i, j)){
				putchar('a'+j-1);
				break;
			}
	}
	return 0;
}

例题十七:方差

思路

更像一道数学题。

方差 \(s^2=\frac{1}{n}\sum_{i=1}^n{(a_i-\overline{a})^2}\),直接硬拆就是 \(-{\overline{a}}^2+\frac{{a_1}^2+{a_2}^2+\ldots+{a_n}^2}{n}\)。于是维护一个区间和的同时维护一个区间平方和。

修改时,一样拆开就是加上了 \(a_{sum}num+num^2(a_r-a_l+1)\)

\(Code\)

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n, m;
int x, y, op;
long double k;
long double a[100005];
struct SegmentTree{
	int l, r;
	long double sum, squ, lazy;
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
	tree[u].squ = tree[u<<1].squ+tree[u<<1|1].squ;
}

inline void pushdown(int u){
	if(tree[u].lazy){
		tree[u<<1].lazy += tree[u].lazy;
		tree[u<<1|1].lazy += tree[u].lazy;
		tree[u<<1].squ += 2*tree[u].lazy*tree[u<<1].sum+tree[u].lazy*tree[u].lazy*(tree[u<<1].r-tree[u<<1].l+1);
		tree[u<<1|1].squ += 2*tree[u].lazy*tree[u<<1|1].sum+tree[u].lazy*tree[u].lazy*(tree[u<<1|1].r-tree[u<<1|1].l+1);
		tree[u<<1].sum += (tree[u<<1].r-tree[u<<1].l+1) * tree[u].lazy;
		tree[u<<1|1].sum += (tree[u<<1|1].r-tree[u<<1|1].l+1) * tree[u].lazy;
		tree[u].lazy = 0;
	}
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].sum = a[l];
		tree[u].squ = a[l]*a[l];
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void modify(int u, int l, int r, long double num){
	if(l <= tree[u].l && r >= tree[u].r){
		tree[u].lazy += num;
		tree[u].squ += 2*num*tree[u].sum+num*num*(tree[u].r-tree[u].l+1);
		tree[u].sum += num*(tree[u].r-tree[u].l+1);
		return;
	}
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	if(l <= mid)	modify(u<<1, l, r, num);
	if(r > mid)	modify(u<<1|1, l, r, num);
	pushup(u);
}

inline long double querysum(int u, int l, int r){
	if(l <= tree[u].l && r >= tree[u].r)	return tree[u].sum;
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	long double ans = 0;
	if(l <= mid)	ans += querysum(u<<1, l, r);
	if(r > mid)	ans += querysum(u<<1|1, l, r);
	return ans;
}

inline long double querysqu(int u, int l, int r){
	if(l <= tree[u].l && r >= tree[u].r)	return tree[u].squ;
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	long double ans = 0;
	if(l <= mid)	ans += querysqu(u<<1, l, r);
	if(r > mid)	ans += querysqu(u<<1|1, l, r);
	return ans;
}

signed main(){
	read(n), read(m);
	for(register int i = 1; i <= n; ++i)	scanf("%LF", &a[i]);
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		read(op);
		if(op == 1){
			read(x), read(y);
			scanf("%LF", &k);
			modify(1, x, y, k);
		}
		if(op == 2){
			read(x), read(y);
			printf("%.4LF\n", querysum(1, x, y)/(y-x+1));
		}
		if(op == 3){
			read(x), read(y);
			long double ans1 = querysqu(1, x, y)/(y-x+1), ans2 = querysum(1, x, y)/(y-x+1);
			printf("%.4LF\n", ans1-ans2*ans2);
		}
	}
	return 0;
}

例题十八:GSS4 - Can you answer these queries IV

思路

除了是多组数据和花神游历各国一模一样,注意判断 \(EOF\) 即可。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
long long a[100005];
int op, x, y;
struct SegmentTree{
	int l, r;
	long long sum, maxn;
}tree[400005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
	tree[u].maxn = max(tree[u<<1].maxn, tree[u<<1|1].maxn);
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].sum = tree[u].maxn = a[l];
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void modify(int u, int l, int r){
	if(tree[u].maxn == 1)	return;
	if(tree[u].l == tree[u].r){
		tree[u].maxn = tree[u].sum = sqrt(tree[u].maxn);
		return;
	}
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	modify(u<<1, l, r);
	if(mid < r)	modify(u<<1|1, l, r);
	pushup(u);
}

inline long long query(int u, int l, int r){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].sum;
	int mid = tree[u].l+tree[u].r >> 1;
	long long ans = 0;
	if(mid >= l)	ans += query(u<<1, l, r);
	if(mid < r)	ans += query(u<<1|1, l, r);
	return ans;
}

int main(){
    int t = 1;
    while(scanf("%d", &n) != EOF){
        memset(tree, 0, sizeof(tree));
        printf("Case #%d:\n", t);
        for(register int i = 1; i <= n; ++i)	read(a[i]);
        build(1, 1, n);
        read(m);
        for(register int i = 1; i <= m; ++i){
            read(op), read(x), read(y);
            if(x > y)	swap(x, y);
            if(!op)	modify(1, x, y);
            else	write(query(1, x, y)), puts("");
        }
        ++t;
        puts("");
    }
	return 0;
}

例题十九:贪婪大陆

思路

乍一看这道题比较像主席树的区间数颜色问题,然而每次修改颜色都会不一样,并且不是修改颜色而是添加颜色,所以这是个区间数线段问题。很难维护一个线段是否在这个区间中是否有交,但是是否不交则是相对容易的,及线段右端点小于区间左端点或者线段左端点大于区间右端点。因此我们可以线段树维护加入线段的两个端点,查询不在区间内出现过的,用加入线段次数减去即可。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
int op, x, y;
struct SegmentTree{
	int l, r;
	int cntl, cntr;
}tree[400005];
int cnt;

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushupl(int u){
	tree[u].cntl = tree[u<<1].cntl + tree[u<<1|1].cntl;
}

inline void pushupr(int u){
	tree[u].cntr = tree[u<<1].cntr + tree[u<<1|1].cntr;
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r)	return;
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
}

inline void modifyl(int u, int pos){
	if(tree[u].l == tree[u].r){
		++tree[u].cntl;
		return;
	}
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= pos)	modifyl(u<<1, pos);
	if(mid < pos)	modifyl(u<<1|1, pos);
	pushupl(u);
}

inline void modifyr(int u, int pos){
	if(tree[u].l == tree[u].r){
		++tree[u].cntr;
		return;
	}
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= pos)	modifyr(u<<1, pos);
	if(mid < pos)	modifyr(u<<1|1, pos);
	pushupr(u);
}

inline int queryl(int u, int l, int r){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].cntl;
	int mid = tree[u].l+tree[u].r >> 1;
	long long ans = 0;
	if(mid >= l)	ans += queryl(u<<1, l, r);
	if(mid < r)	ans += queryl(u<<1|1, l, r);
	return ans;
}

inline int queryr(int u, int l, int r){
	if(tree[u].l >= l && tree[u].r <= r)	return tree[u].cntr;
	int mid = tree[u].l+tree[u].r >> 1;
	long long ans = 0;
	if(mid >= l)	ans += queryr(u<<1, l, r);
	if(mid < r)	ans += queryr(u<<1|1, l, r);
	return ans;
}

int main(){
	read(n), read(m);
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		read(op), read(x), read(y);
		if(op == 1){
			modifyl(1, x);
			modifyr(1, y);
			++cnt;
		}
		if(op == 2){
			int ans1 = 0, ans2 = 0;
			if(x != 1)	ans1 = queryr(1, 1, x-1);
			if(y != n)	ans2 = queryl(1, y+1, n);
			write(cnt-ans1-ans2);
			puts("");
		}
	}
	return 0;
}

例题二十:Nastya and King-Shamans

思路

以为题目有负数,没有负数就好处理很多了
如果答案非 \(0\),那么此时答案个数将不超过 \(\log a\) 个,因为每次 \(a_i=s_{i-1}\)\(s_i\) 会翻一倍。

因此有一个较为巧妙的作法,因为 \(a_i-sum_{i-1} > 0\) 个数不超过 \(\log 10^9\),所以直接暴力去维护查找满足这样条件的点是否存在即可,时间复杂度 \(q\log n\log a\),线段树维护 \(a_i-sum_{i-1}\) 的最大值与区间和,需要支持区间修改即可。

如果答案等于 \(0\) 有贡献,那么毫无疑问的是 \(a_1=0\),因此只需要特判 \(a_1\) 是否为 \(0\) 即可。

当然还有一种线段树二分的作法,时间上更优为 \(n\log^2n\),但是不会线段树二分。

\(Code\)

#include<bits/stdc++.h>
#define inf 1e15
using namespace std;

int n, q;
long long a[200005], sum[200005];
int x, y;
struct SegmentTree{
	int l, r;
	long long maxn, lazy;
}tree[800005];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].maxn = max(tree[u<<1].maxn, tree[u<<1|1].maxn);
}

inline void pushdown(int u){
	if(tree[u].lazy){
		tree[u<<1].maxn += tree[u].lazy, tree[u<<1|1].maxn += tree[u].lazy;
		tree[u<<1].lazy += tree[u].lazy, tree[u<<1|1].lazy += tree[u].lazy;
		tree[u].lazy = 0;
	}
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].maxn = a[l]-sum[l-1];
		return;
	}
	int mid = l+r >> 1;
	build(u<<1, l, mid);
	build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void modify(int u, int l, int r, long long num){
	if(tree[u].l >= l && tree[u].r <= r){
		tree[u].maxn += num, tree[u].lazy += num;
		return;
	}
	pushdown(u);
	int mid = tree[u].l+tree[u].r >> 1;
	if(mid >= l)	modify(u<<1, l, r, num);
	if(mid < r)	modify(u<<1|1, l, r, num);
	pushup(u);
}

inline int query(int u){
	if(tree[u].l == tree[u].r)	return tree[u].maxn == 0?tree[u].l:-1;
	pushdown(u);
	int ans = 0;
	if(tree[u<<1].maxn >= 0)	ans = query(u<<1);
	if(tree[u<<1|1].maxn >= 0)	ans = max(ans, query(u<<1|1));
	if(ans)	return ans;
	return -1;
}

int main(){
	read(n), read(q);
	for(register int i = 1; i <= n; ++i)	read(a[i]), sum[i] = sum[i-1]+a[i];
	build(1, 1, n);
	for(register int i = 1; i <= q; ++i){
		read(x), read(y);
		modify(1, x, x, y-a[x]);
		if(x < n)	modify(1, x+1, n, a[x]-y);
		a[x] = y;
		if(a[1] == 0)	puts("1");
		else{
			write(query(1));
			puts("");
		}
	}
	return 0;
}

例题二十一:序列操作

思路

几个操作都是比较肉眼可见的,打个标记就行了。只不过 \(pushup,pushdown\) 代码略显冗长,线段树中的模拟题,细节是相当多的,调了半天才调出来。

记录两个标记,\(lazy\)\(rev\),表示是否有区间的整体修改和取反。合并时就是比较常见的左区间最大值,右区间最大值和左右合并的最大值。需要维护前缀最长 \(0,1\),后缀最长 \(0,1\),整体最长 \(0,1\)。因为有取反操作,所以也需要记录 \(0\)\(pushup\) 基本就是这几个操作,类似于 \(step\) 那道题。比较毒瘤的是 \(pushdown\),因为取反和整体赋值是有优先级的,如果当前已经有整体赋值了那么取反是可以忽略的,如果当前有取反了那么下放是需要判断儿子的区间赋值操作,如果已经有区间赋值那么就要对区间赋值取反,没有就维护取反标记即可。也正是因为操作的优先级问题,还要在修改时先执行 \(pushdown\) 操作,防止前面的懒标记被覆盖。当然查询时就不必要了。对于查询最长连续 \(1\) 操作,就类似于维护整个线段树的维护方式,所以在树上传递时传递的是结构体 \(SegmentTree\)

代码实现略有丑陋,其实重载线段树合并会让代码简短很多。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
bool a[100005];
int op, x, y;

struct SegmentTree{
    int l, r;
    int sum;
    int maxn[2], maxl[2], maxr[2];
    short lazy;
    bool rev;
}tree[400005];

template<typename T>
inline void read(T&x){
    x = 0; char q; bool f = 1;
    while(!isdigit(q = getchar()))  if(q == '-')    f = 0;
    while(isdigit(q)){
        x = (x<<1) + (x<<3) + (q^48);
        q = getchar();
    }
    x = f?x:-x;
}

template<typename T>
inline void write(T x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)	write(x/10);
    putchar(x%10+'0');
}

inline void pushup(int u){
    tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
    tree[u].maxl[0] = tree[u<<1].maxl[0];
    if(tree[u<<1].sum == 0) tree[u].maxl[0] += tree[u<<1|1].maxl[0];
    tree[u].maxl[1] = tree[u<<1].maxl[1];
    if(tree[u<<1].sum == tree[u<<1].r-tree[u<<1].l+1)   tree[u].maxl[1] += tree[u<<1|1].maxl[1];
    tree[u].maxr[0] = tree[u<<1|1].maxr[0];
    if(tree[u<<1|1].sum == 0)   tree[u].maxr[0] += tree[u<<1].maxr[0];
    tree[u].maxr[1] = tree[u<<1|1].maxr[1];
    if(tree[u<<1|1].sum == tree[u<<1|1].r-tree[u<<1|1].l+1) tree[u].maxr[1] += tree[u<<1].maxr[1];
    tree[u].maxn[0] = max(tree[u<<1].maxr[0]+tree[u<<1|1].maxl[0], max(tree[u<<1].maxn[0], tree[u<<1|1].maxn[0]));
    tree[u].maxn[1] = max(tree[u<<1].maxr[1]+tree[u<<1|1].maxl[1], max(tree[u<<1].maxn[1], tree[u<<1|1].maxn[1]));
}

inline void pushdown(int u){
    if(tree[u].lazy == 0){
        tree[u].rev = 0;
        tree[u<<1].lazy = tree[u<<1|1].lazy = 0;
        tree[u<<1].sum = tree[u<<1|1].sum = 0;
        tree[u<<1].maxl[1] = tree[u<<1].maxr[1] = tree[u<<1|1].maxl[1] = tree[u<<1|1].maxr[1] = tree[u<<1].maxn[1] = tree[u<<1|1].maxn[1] = 0;
        tree[u<<1].maxl[0] = tree[u<<1].maxr[0] = tree[u<<1].maxn[0] = tree[u<<1].r-tree[u<<1].l+1;
        tree[u<<1|1].maxl[0] = tree[u<<1|1].maxr[0] = tree[u<<1|1].maxn[0] = tree[u<<1|1].r-tree[u<<1|1].l+1;
        tree[u].lazy = -1;
    }
    if(tree[u].lazy == 1){
        tree[u].rev = 0;
        tree[u<<1].lazy = tree[u<<1|1].lazy = 1;
        tree[u<<1].sum = tree[u<<1].maxl[1] = tree[u<<1].maxr[1] = tree[u<<1].maxn[1] = tree[u<<1].r-tree[u<<1].l+1;
        tree[u<<1|1].sum = tree[u<<1|1].maxl[1] = tree[u<<1|1].maxr[1] = tree[u<<1|1].maxn[1] = tree[u<<1|1].r-tree[u<<1|1].l+1;
        tree[u<<1].maxl[0] = tree[u<<1].maxr[0] = tree[u<<1].maxn[0] = 0;;
        tree[u<<1|1].maxl[0] = tree[u<<1|1].maxr[0] = tree[u<<1|1].maxn[0] = 0;
        tree[u].lazy = -1;
    }
    if(tree[u].rev){
		if(tree[u<<1].lazy != -1)	tree[u<<1].lazy ^= 1;
		else	tree[u<<1].rev ^= 1;
		if(tree[u<<1|1].lazy != -1)	tree[u<<1|1].lazy ^= 1;
		else	tree[u<<1|1].rev ^= 1;
        tree[u<<1].sum = tree[u<<1].r-tree[u<<1].l+1-tree[u<<1].sum, tree[u<<1|1].sum = tree[u<<1|1].r-tree[u<<1|1].l+1-tree[u<<1|1].sum;
        swap(tree[u<<1].maxl[0], tree[u<<1].maxl[1]), swap(tree[u<<1|1].maxl[0], tree[u<<1|1].maxl[1]);
        swap(tree[u<<1].maxr[0], tree[u<<1].maxr[1]), swap(tree[u<<1|1].maxr[0], tree[u<<1|1].maxr[1]);
        swap(tree[u<<1].maxn[0], tree[u<<1].maxn[1]), swap(tree[u<<1|1].maxn[0], tree[u<<1|1].maxn[1]);
        tree[u].rev = 0;
    }
}

inline void build(int u, int l, int r){
    tree[u].l = l, tree[u].r = r, tree[u].lazy = -1;
    if(l == r){
        tree[u].sum = a[l];
		tree[u].maxn[a[l]] = tree[u].maxl[a[l]] = tree[u].maxr[a[l]] = 1;
        return;
    }
    int mid = l+r >> 1;
    build(u<<1, l, mid);
    build(u<<1|1, mid+1, r);
    pushup(u);
}

inline void modify0(int u, int l, int r){
	pushdown(u);
    if(tree[u].l >= l && tree[u].r <= r){
        tree[u].sum = 0;
        tree[u].maxn[0] = tree[u].maxl[0] = tree[u].maxr[0] = tree[u].r-tree[u].l+1;
        tree[u].maxn[1] = tree[u].maxl[1] = tree[u].maxr[1] = 0;
        tree[u].lazy = 0;
        return;
    }
    pushdown(u);
    int mid = tree[u].l+tree[u].r >> 1;
    if(mid >= l)    modify0(u<<1, l, r);
    if(mid < r) modify0(u<<1|1, l, r);
    pushup(u);
}

inline void modify1(int u, int l, int r){
	pushdown(u);
    if(tree[u].l >= l && tree[u].r <= r){
        tree[u].sum = tree[u].r-tree[u].l+1;
        tree[u].maxn[1] = tree[u].maxl[1] = tree[u].maxr[1] = tree[u].r-tree[u].l+1;
        tree[u].maxn[0] = tree[u].maxl[0] = tree[u].maxr[0] = 0;
        tree[u].lazy = 1;
        return;
    }
    int mid = tree[u].l+tree[u].r >> 1;
    if(mid >= l)    modify1(u<<1, l, r);
    if(mid < r) modify1(u<<1|1, l, r);
    pushup(u);
}

inline void reverse(int u, int l, int r){
	pushdown(u);
    if(tree[u].l >= l && tree[u].r <= r){
        tree[u].rev ^= 1;
        tree[u].sum = tree[u].r-tree[u].l+1-tree[u].sum;
        swap(tree[u].maxl[0], tree[u].maxl[1]), swap(tree[u].maxr[0], tree[u].maxr[1]), swap(tree[u].maxn[0], tree[u].maxn[1]);
        return;
    }
    int mid = tree[u].l+tree[u].r >> 1;
    if(mid >= l)    reverse(u<<1, l, r);
    if(mid < r) reverse(u<<1|1, l, r);
    pushup(u);
}

inline int query1(int u, int l, int r){
    if(tree[u].l >= l && tree[u].r <= r)    return tree[u].sum;
	pushdown(u);
    int mid = tree[u].l+tree[u].r >> 1, ans = 0;
    if(mid >= l)    ans += query1(u<<1, l, r);
    if(mid < r) ans += query1(u<<1|1, l, r);
    return ans;
}

inline SegmentTree query2(int u, int l, int r){
    if(tree[u].l >= l && tree[u].r <= r)    return tree[u];
	pushdown(u);
    int mid = tree[u].l+tree[u].r >> 1;
    if(mid < l) return query2(u<<1|1, l, r);
    if(mid >= r)    return query2(u<<1, l, r);
	SegmentTree ans, ansl = query2(u<<1, l, r), ansr = query2(u<<1|1, l, r);
	ans.l = ansl.l, ans.r = ansr.r;
	ans.sum = ansl.sum+ansr.sum;
    ans.maxl[0] = ansl.maxl[0];
    if(ansl.sum == 0)   ans.maxl[0] += ansr.maxl[0];
    ans.maxl[1] = ansl.maxl[1];
    if(ansl.sum == ansl.r-ansl.l+1)	ans.maxl[1] += ansr.maxl[1];
    ans.maxr[0] = ansr.maxr[0];
    if(ansr.sum == 0)   ans.maxr[0]	+= ansl.maxr[0];
    ans.maxr[1] = ansr.maxr[1];
    if(ansr.sum == ansr.r-ansr.l+1)	ans.maxr[1] += ansl.maxr[1];
    ans.maxn[0] = max(ansl.maxr[0]+ansr.maxl[0], max(ansl.maxn[0], ansr.maxn[0]));
    ans.maxn[1] = max(ansl.maxr[1]+ansr.maxl[1], max(ansl.maxn[1], ansr.maxn[1]));
    return ans;
}

int main(){
	read(n), read(m);
    for(register int i = 1; i <= n; ++i)    read(a[i]);
    build(1, 1, n);
    for(register int i = 1; i <= m; ++i){
        read(op), read(x), read(y);
        if(op == 0) modify0(1, x+1, y+1);
        if(op == 1) modify1(1, x+1, y+1);
        if(op == 2) reverse(1, x+1, y+1);
        if(op == 3) write(query1(1, x+1, y+1)), puts("");
        if(op == 4) write(query2(1, x+1, y+1).maxn[1]), puts("");
    }
    return 0;
}

例题二十二:GSS3 - Can you answer these queries III

思路

和上面那道题有异曲同工之妙,重点在于区间查询时也要像线段树一样维护答案的 \(maxn\),当然比上面那道良心多了。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
int a[50005];
int op, x, y;

struct SegmentTree{
    int l, r;
	long long maxl, maxr, maxn, sum;
}tree[200005];

template<typename T>
inline void read(T&x){
    x = 0; char q; bool f = 1;
    while(!isdigit(q = getchar()))  if(q == '-')    f = 0;
    while(isdigit(q)){
        x = (x<<1) + (x<<3) + (q^48);
        q = getchar();
    }
    x = f?x:-x;
}

template<typename T>
inline void write(T x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)	write(x/10);
    putchar(x%10+'0');
}

inline void pushup(int u){
	tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
    tree[u].maxl = max(tree[u<<1].maxl, tree[u<<1].sum+tree[u<<1|1].maxl);
	tree[u].maxr = max(tree[u<<1|1].maxr, tree[u<<1|1].sum+tree[u<<1].maxr);
	tree[u].maxn = max(tree[u<<1].maxr+tree[u<<1|1].maxl, max(tree[u<<1].maxn, tree[u<<1|1].maxn));
}

inline void build(int u, int l, int r){
    tree[u].l = l, tree[u].r = r;
    if(l == r){
		tree[u].sum = tree[u].maxn = tree[u].maxl = tree[u].maxr = a[l];
        return;
    }
    int mid = l+r >> 1;
    build(u<<1, l, mid);
    build(u<<1|1, mid+1, r);
    pushup(u);
}

inline void modify(int u, int pos, int num){
    if(tree[u].l == tree[u].r){
		tree[u].sum = tree[u].maxn = tree[u].maxl = tree[u].maxr = num;
        return;
    }
    int mid = tree[u].l+tree[u].r >> 1;
    if(mid >= pos)	modify(u<<1, pos, num);
    if(mid < pos)	modify(u<<1|1, pos, num);
    pushup(u);
}

inline SegmentTree query(int u, int l, int r){
    if(tree[u].l >= l && tree[u].r <= r)    return tree[u];
    int mid = tree[u].l+tree[u].r >> 1;
	if(mid < l)	return query(u<<1|1, l, r);
	if(mid >= r)	return query(u<<1, l, r);
	SegmentTree ans, ansl = query(u<<1, l, r), ansr = query(u<<1|1, l, r);
	ans.sum = ansl.sum+ansr.sum;
	ans.maxl = max(ansl.maxl, ansl.sum+ansr.maxl);
	ans.maxr = max(ansr.maxr, ansr.sum+ansl.maxr);
	ans.maxn = max(ansl.maxr+ansr.maxl, max(ansl.maxn, ansr.maxn));
    return ans;
}

int main(){
	read(n);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	read(m);
	for(register int i = 1; i <= m; ++i){
		read(op), read(x), read(y);
		if(!op)	modify(1, x, y);
		else	write(query(1, x, y).maxn), puts("");
	}
    return 0;
}

例题二十三:开关

思路

对于 \(01\) 串区间取反的版题,维护一个 \(lazy\) 表示区间取反懒标记,反正是序列操作的一个子问题。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
int op, x, y;

struct SegmentTree{
    int l, r;
	int sum;
	bool lazy;
}tree[400005];

template<typename T>
inline void read(T&x){
    x = 0; char q; bool f = 1;
    while(!isdigit(q = getchar()))  if(q == '-')    f = 0;
    while(isdigit(q)){
        x = (x<<1) + (x<<3) + (q^48);
        q = getchar();
    }
    x = f?x:-x;
}

template<typename T>
inline void write(T x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)	write(x/10);
    putchar(x%10+'0');
}

inline void pushup(int u){
	tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
}

inline void pushdown(int u){
	if(tree[u].lazy){
		tree[u<<1].lazy ^= 1, tree[u<<1|1].lazy ^= 1;
		tree[u<<1].sum = tree[u<<1].r-tree[u<<1].l+1-tree[u<<1].sum, tree[u<<1|1].sum = tree[u<<1|1].r-tree[u<<1|1].l+1-tree[u<<1|1].sum;
		tree[u].lazy = 0;
	}
}

inline void build(int u, int l, int r){
    tree[u].l = l, tree[u].r = r;
	if(l == r)	return;
    int mid = l+r >> 1;
    build(u<<1, l, mid);
    build(u<<1|1, mid+1, r);
}

inline void modify(int u, int l, int r){
    if(tree[u].l >= l && tree[u].r <= r){
		tree[u].sum = tree[u].r-tree[u].l+1-tree[u].sum;
		tree[u].lazy ^= 1;
        return;
    }
	pushdown(u);
    int mid = tree[u].l+tree[u].r >> 1;
    if(mid >= l)	modify(u<<1, l, r);
    if(mid < r)	modify(u<<1|1, l, r);
    pushup(u);
}

inline int query(int u, int l, int r){
    if(tree[u].l >= l && tree[u].r <= r)    return tree[u].sum;
	pushdown(u);
    int mid = tree[u].l+tree[u].r >> 1, ans = 0;
	if(mid >= l)	ans += query(u<<1, l, r);
	if(mid < r)	ans += query(u<<1|1, l, r);
    return ans;
}

int main(){
	read(n), read(m);
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		read(op), read(x), read(y);
		if(!op)	modify(1, x, y);
		else	write(query(1, x, y)), puts("");
	}
    return 0;
}

例题二十四:haybalesCounting Haybale P

思路

区间加区间求 \(\max,sum\),一道水题。

\(Code\)

#include<bits/stdc++.h>
#define inf 1e17
using namespace std;

int n, m;
long long a[200005];
char op;
long long x, y, z;

struct SegmentTree{
    int l, r;
	long long sum, minn, lazy;
}tree[800005];

template<typename T>
inline void read(T&x){
    x = 0; char q; bool f = 1;
    while(!isdigit(q = getchar()))  if(q == '-')    f = 0;
    while(isdigit(q)){
        x = (x<<1) + (x<<3) + (q^48);
        q = getchar();
    }
    x = f?x:-x;
}

template<typename T>
inline void write(T x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)	write(x/10);
    putchar(x%10+'0');
}

inline void pushup(int u){
	tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
	tree[u].minn = min(tree[u<<1].minn, tree[u<<1|1].minn);
}

inline void pushdown(int u){
	if(tree[u].lazy){
		tree[u<<1].lazy += tree[u].lazy, tree[u<<1|1].lazy += tree[u].lazy;
		tree[u<<1].sum += (tree[u<<1].r-tree[u<<1].l+1)*tree[u].lazy, tree[u<<1|1].sum += (tree[u<<1|1].r-tree[u<<1|1].l+1)*tree[u].lazy;
		tree[u<<1].minn += tree[u].lazy, tree[u<<1|1].minn += tree[u].lazy;
		tree[u].lazy = 0;
	}
}

inline void build(int u, int l, int r){
    tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].minn = tree[u].sum = a[l];
		return;
	}
    int mid = l+r >> 1;
    build(u<<1, l, mid);
    build(u<<1|1, mid+1, r);
	pushup(u);
}

inline void modify(int u, int l, int r, long long num){
    if(tree[u].l >= l && tree[u].r <= r){
		tree[u].sum += (tree[u].r-tree[u].l+1)*num;
		tree[u].minn += num;
		tree[u].lazy += num;
        return;
    }
	pushdown(u);
    int mid = tree[u].l+tree[u].r >> 1;
    if(mid >= l)	modify(u<<1, l, r, num);
    if(mid < r)	modify(u<<1|1, l, r, num);
    pushup(u);
}

inline long long queryminn(int u, int l, int r){
    if(tree[u].l >= l && tree[u].r <= r)    return tree[u].minn;
	pushdown(u);
    int mid = tree[u].l+tree[u].r >> 1;
	long long ans = inf;
	if(mid >= l)	ans = min(ans, queryminn(u<<1, l, r));
	if(mid < r)	ans = min(ans, queryminn(u<<1|1, l, r));
    return ans;
}

inline long long querysum(int u, int l, int r){
    if(tree[u].l >= l && tree[u].r <= r)    return tree[u].sum;
	pushdown(u);
    int mid = tree[u].l+tree[u].r >> 1;
	long long ans = 0;
	if(mid >= l)	ans += querysum(u<<1, l, r);
	if(mid < r)	ans += querysum(u<<1|1, l, r);
    return ans;
}

int main(){
	read(n), read(m);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	for(register int i = 1; i <= m; ++i){
		scanf("%c", &op);
		if(op == 'M'){
			read(x), read(y);
			write(queryminn(1, x, y)), puts("");
		}
		if(op == 'P'){
			read(x), read(y), read(z);
			modify(1, x, y, z);
		}
		if(op == 'S'){
			read(x), read(y);
			write(querysum(1, x, y)), puts("");
		}
	}
    return 0;
}

例题二十五:GSS1 - Can you answer these queries I

思路

不带修版本的 \(GSS3\),可能还有别的作法只是没想到。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
int a[50005];
int x, y;

struct SegmentTree{
    int l, r;
	long long maxl, maxr, maxn, sum;
}tree[200005];

template<typename T>
inline void read(T&x){
    x = 0; char q; bool f = 1;
    while(!isdigit(q = getchar()))  if(q == '-')    f = 0;
    while(isdigit(q)){
        x = (x<<1) + (x<<3) + (q^48);
        q = getchar();
    }
    x = f?x:-x;
}

template<typename T>
inline void write(T x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)	write(x/10);
    putchar(x%10+'0');
}

inline void pushup(int u){
	tree[u].sum = tree[u<<1].sum+tree[u<<1|1].sum;
    tree[u].maxl = max(tree[u<<1].maxl, tree[u<<1].sum+tree[u<<1|1].maxl);
	tree[u].maxr = max(tree[u<<1|1].maxr, tree[u<<1|1].sum+tree[u<<1].maxr);
	tree[u].maxn = max(tree[u<<1].maxr+tree[u<<1|1].maxl, max(tree[u<<1].maxn, tree[u<<1|1].maxn));
}

inline void build(int u, int l, int r){
    tree[u].l = l, tree[u].r = r;
    if(l == r){
		tree[u].sum = tree[u].maxn = tree[u].maxl = tree[u].maxr = a[l];
        return;
    }
    int mid = l+r >> 1;
    build(u<<1, l, mid);
    build(u<<1|1, mid+1, r);
    pushup(u);
}

inline void modify(int u, int pos, int num){
    if(tree[u].l == tree[u].r){
		tree[u].sum = tree[u].maxn = tree[u].maxl = tree[u].maxr = num;
        return;
    }
    int mid = tree[u].l+tree[u].r >> 1;
    if(mid >= pos)	modify(u<<1, pos, num);
    if(mid < pos)	modify(u<<1|1, pos, num);
    pushup(u);
}

inline SegmentTree query(int u, int l, int r){
    if(tree[u].l >= l && tree[u].r <= r)    return tree[u];
    int mid = tree[u].l+tree[u].r >> 1;
	if(mid < l)	return query(u<<1|1, l, r);
	if(mid >= r)	return query(u<<1, l, r);
	SegmentTree ans, ansl = query(u<<1, l, r), ansr = query(u<<1|1, l, r);
	ans.sum = ansl.sum+ansr.sum;
	ans.maxl = max(ansl.maxl, ansl.sum+ansr.maxl);
	ans.maxr = max(ansr.maxr, ansr.sum+ansl.maxr);
	ans.maxn = max(ansl.maxr+ansr.maxl, max(ansl.maxn, ansr.maxn));
    return ans;
}

int main(){
	read(n);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	read(m);
	for(register int i = 1; i <= m; ++i){
		read(x), read(y);
		write(query(1, x, y).maxn), puts("");
	}
    return 0;
}

例题二十六:\(Divan\) \(and\) \(a\) \(Cottage\)

思路

权值线段树动态开点+线段树上二分。

有这样一个显然的结论:若 \(a_i<a_j\),则 \(ans_i\leq ans_j\)

于是我们可以发现这样一个性质:每一天固定的室外温度对室内不同温度的影响可能有三段的,从 \(0\) ~ \(t_i-1\) 上全部加一, \(t_i\) 不变, \(t_i+1\) ~ \(10^9\) 全部减一,于是可以在线段树上二分,查找增加和减小的区间,线段树记录区间上的 \(minn\)\(maxn\) 即可,整棵权值线段树都呈单调不降。于是每次查找从 \(1\) 开始到最后一个小于 \(t_i\) 的数,并全部加一,从 \(n\) 开始向前到第一个大于 \(t_i\) 的数,并全部减一。边界条件比较恶心,可以特判,如果是边界就只增或只减。可以加深对权值线段树的理解,主要还是结论题。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

const int mod = 1e9+1;
int n;
int t, k;
int q, lastans;
int cnt, root;
struct SegmentTree{
	int lson, rson, l, r;
	int maxn, minn, lazy;
}tree[200005<<6];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

inline void pushup(int u){
	tree[u].maxn = tree[tree[u].rson].maxn, tree[u].minn = tree[tree[u].lson].minn;
}

inline void build(int &u, int l, int r){
	u = ++cnt;
	tree[u].minn = l, tree[u].maxn = r;
}

inline void pushdown(int u, int l, int r){
	int mid = l+r >> 1;
	if(!tree[u].lson)	build(tree[u].lson, l, mid);
	if(!tree[u].rson)	build(tree[u].rson, mid+1, r);
	tree[tree[u].lson].maxn += tree[u].lazy, tree[tree[u].lson].minn += tree[u].lazy, tree[tree[u].lson].lazy += tree[u].lazy;
	tree[tree[u].rson].maxn += tree[u].lazy, tree[tree[u].rson].minn += tree[u].lazy, tree[tree[u].rson].lazy += tree[u].lazy;
	tree[u].lazy = 0;
}

inline void modify(int &u, int tl, int tr, int l, int r, int x){
	if(!u)	build(u, l, r);
	if(l <= tl && r >= tr){
		tree[u].maxn += x, tree[u].minn += x, tree[u].lazy += x;
		return;
	}
	pushdown(u, tl, tr);
	int mid = tl+tr >> 1;
	if(mid >= l)	modify(tree[u].lson, tl, mid, l, r, x);
	if(mid < r)	modify(tree[u].rson, mid+1, tr, l, r, x);
	pushup(u);
}

inline int searchl(int u, int l, int r, int x){
	if(l == r)	return l;
	pushdown(u, l, r);
	int mid = l+r >> 1;
	if(tree[tree[u].rson].minn < x)	searchl(tree[u].rson, mid+1, r, x);
	else	searchl(tree[u].lson, l, mid, x);
}

inline int searchr(int u, int l, int r, int x){
	if(l == r)	return l;
	pushdown(u, l, r);
	int mid = l+r >> 1;
	if(tree[tree[u].lson].maxn > x)	searchr(tree[u].lson, l, mid, x);
	else	searchr(tree[u].rson, mid+1, r, x);
}

inline int search(int u, int l, int r, int x){
	if(l == r)	return tree[u].maxn;
	pushdown(u, l, r);
	int mid = l+r >> 1;
	if(mid >= x)	search(tree[u].lson, l, mid, x);
	else	search(tree[u].rson, mid+1, r, x);
}

int main(){
	read(n);
	build(root, 0, 1e9);
	for(register int i = 1; i <= n; ++i){
		read(t), read(k);
		int l = -1, r = -1;
		if(tree[root].maxn > t)	r = searchr(root, 0, 1e9, t);
		if(tree[root].minn < t)	l = searchl(root, 0, 1e9, t);
		if(tree[root].maxn > t)	modify(root, 0, 1e9, r, 1e9, -1);
		if(tree[root].minn < t)	modify(root, 0, 1e9, 0, l, 1);
		for(register int j = 1; j <= k; ++j){
			read(q);
			q = (q+lastans)%mod;
			lastans = search(root, 0, 1e9, q);
			write(lastans);
			puts("");
		}
	}
	return 0;
}
posted @ 2021-11-19 16:39  Zzzzzzzm  阅读(85)  评论(0)    收藏  举报