洛谷线段树题解

基础题链接:https://blog.csdn.net/qq_48344603/article/details/107746383

P3372 【模板】线段树 1

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
ll tree[MAXN * 4];
ll add[MAXN * 4];
ll num[MAXN];
int n, m;
void buildtree(int left,int right,int r) {
	if (left == right) {
		tree[r] = num[left];
		return;
	}
	int mid = (left + right) / 2;
	buildtree(left, mid, 2 * r);
	buildtree(mid + 1,right, 2 * r + 1);
	tree[r] = tree[2 * r] + tree[2 * r + 1];
}
void push_down(ll left,ll right, int u) {
	ll len = right - left + 1;
	tree[2 * u] += add[u] * (len - len / 2);
	tree[2 * u + 1] += add[u] * (len / 2);
	add[2 * u] += add[u];
	add[2 * u + 1] += add[u];
	add[u] = 0;
}
void update(ll l, ll r, ll tar, int left, int right, int u) {
	if (left >= l && right <= r) {
		tree[u] += tar * (right - left + 1);
		add[u] += tar;
		return;
	}
	if (add[u]) {
		push_down(left, right, u);
	}
	int mid = (left + right) / 2;
	if (l <= mid)update(l, r, tar, left, mid, 2 * u);
	if (r > mid)update(l, r, tar, mid + 1, right, 2 * u + 1);
	tree[u] = tree[2 * u] + tree[2 * u + 1];
}
ll query(ll l, ll r, int left, int right, int u) {
	if (l <= left && r >= right) {
		return tree[u];
	}
	if (add[u]) {
		push_down(left, right, u);
	}
	int mid = (left + right) / 2;
	ll sum = 0;
	if (l <= mid)sum += query(l, r, left, mid, 2 * u);
	if (r > mid)sum += query(l, r, mid + 1, right, 2 * u + 1);
	return sum;
	
}
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for (int i = 1;i <= n;i++) {
		cin >> num[i];
	}
	buildtree(1, n, 1);
	for (int i = 1;i <= m;i++) {
		int c;
		cin >> c;
		if (c == 1) {
			ll left, right, tar;
			cin >> left >> right >> tar;
			update(left, right, tar, 1, n, 1);
		}
		if (c == 2) {
			ll left,right;
			cin >> left >> right;
			cout << query(left, right, 1, n, 1) << endl;
		}
	}
	return 0;
}

P3373 【模板】线段树 2

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
ll tree[MAXN * 4];
ll mul[MAXN * 4];
ll add[MAXN * 4];
ll num[MAXN];
int n, m, p;
//大部分都可以看题解
void push_down(int left, int right, int u) {
	int len = right - left + 1;
	tree[u << 1] = tree[u << 1] * mul[u] % p + add[u] * (len - len / 2);
	tree[u << 1] %= p;
	tree[u << 1 | 1] = tree[u << 1 | 1] * mul[u] % p + add[u] * (len / 2);
	tree[u << 1 | 1] %= p;
	mul[u << 1] *= mul[u], mul[u << 1] %= p;
	mul[u << 1 | 1] *= mul[u], mul[u << 1 | 1] %= p;
	//这部分做一些解释
	//为何在左右子树的add里也要乘上mul,不是在mul操作时已经改了嘛?
	//其实这2个操作不同,update2的操作只是对当前树的操作,而这里是对子树的操作,假设原来的子树就有add,现在我们从root往下down时,我们的mul只是把root的add操作了,并未对子树的add操作。
	add[u << 1] = add[u << 1] * mul[u] + add[u];
	add[u << 1 | 1] = add[u << 1 | 1] * mul[u] + add[u];
	add[u << 1 | 1] %= p;
	add[u << 1] %= p;
	add[u] = 0;
	mul[u] = 1;
}
void buildtree(int left, int right, int u) {
	if (left == right) {
		tree[u] = num[left];
		return;
	}
	int mid=(left + right) / 2;
	buildtree(left, mid, 2 * u);
	buildtree(mid+1,right, 2 * u + 1);
	tree[u] = (tree[2 * u] + tree[2 * u + 1]) %p;
}
void update1(ll l, ll r, ll tar, int left, int right, int u) {
	if (l <= left && r >= right) {
		tree[u] = (tree[u] + tar * (right - left + 1))%p;
		add[u] += tar;
		add[u] %= p;
		return;
	}
	push_down(left, right, u);
	int mid = (left + right) / 2;
	if (l <= mid)update1(l,r, tar, left,mid, u<<1);
	if (r > mid)update1(l,r, tar, mid+1,right, u << 1 | 1);
	tree[u] = tree[u << 1] + tree[u << 1 | 1];
	tree[u] %= p;
}
void update2(ll l, ll r, ll tar, int left, int right, int u) {
	if (l <= left && r >= right) {
	//这是乘法分配律
		add[u] *= tar;
		add[u] %= p;
		tree[u] *= tar;
		tree[u] %= p;
		mul[u] *= tar;
		mul[u] %= p;
		return;
	}
	push_down(left, right, u);
	int mid = (left + right) / 2;
	if (l <= mid)update2(l, r, tar, left, mid, u << 1);
	if (r > mid)update2(l, r, tar, mid + 1, right, u << 1 | 1);
	tree[u] = tree[u << 1] + tree[u << 1 | 1];
	tree[u] %= p;
}

ll query(ll l,ll r,int left, int right, int u) {
	if (l <= left && r >= right) {
		return tree[u];
	}
	push_down(left, right, u);
	int mid = (left + right) / 2;
	ll sum = 0;
	if (l <= mid)sum = (sum + query(l, r, left, mid, u << 1))%p;
	if (r > mid)sum = (sum + query(l, r, mid + 1, right, u << 1 | 1)) % p;
	return sum;
}
int main() {
	ios::sync_with_stdio(false);
	cin >> n >> m >> p;
	for (int i = 1;i <= 4 * n;i++)mul[i] = 1;
	for (int i = 1;i <= n;i++) {
		cin >> num[i];
		num[i] %= p;
	}
	buildtree(1, n, 1);
	for (int i = 1;i <= m;i++) {
		int c;
		cin >> c;
		if (c == 1) {
			ll l, r, tar;
			cin >> l >> r >> tar;
			update2(l, r, tar, 1, n, 1);
		}
		if (c == 2) {
			ll l, r, tar;
			cin >> l >> r >> tar;
			update1(l, r, tar, 1, n, 1);
		}
		if (c == 3) {
			ll l, r;
			cin >> l >> r;
			cout << query(l, r, 1, n, 1) << endl;
		}
	}
	return 0;
}

P2023 [AHOI2009] 维护序列

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
ll tree[MAXN * 4];
ll mul[MAXN * 4];
ll add[MAXN * 4];
ll num[MAXN];
int n, m, p;
//和2相同
void push_down(int left,int right,int u) {
	int len = (right - left + 1);
	tree[u << 1] = (tree[u << 1]*mul[u] + add[u] * (len-len/2)) % p;
	tree[u << 1 | 1] = (tree[u << 1 | 1] * mul[u] + add[u] * (len / 2)) % p;
	mul[u << 1] = (mul[u << 1] * mul[u]) % p;
	mul[u << 1 | 1] = (mul[u << 1 | 1] * mul[u]) % p;
	add[u << 1] = (add[u << 1] * mul[u] + add[u]) % p;
	add[u << 1|1] = (add[u << 1|1] * mul[u] + add[u]) % p;
	mul[u] = 1;
	add[u] = 0;
}
void buildtree(int left, int right, int u) {
	if (left == right) {
		tree[u] = num[left];
		return;
	}
	int mid = (left + right) >> 1;
	buildtree(left, mid, u << 1);
	buildtree(mid + 1, right, u << 1 | 1);
	tree[u] = (tree[u << 1] + tree[u << 1 | 1])%p;
}
void update1(ll l, ll r,ll tar, int left, int right, int u) {
	if (l <= left && r >= right) {
		add[u] *= tar;
		add[u] %= p;
		tree[u] *= tar;
		tree[u] %= p;
		mul[u] *= tar;
		mul[u] %= p;
		return;
	}
	push_down(left,right,u);
	int mid = (left + right)>>1;
	if (l <= mid)update1(l, r, tar, left, mid, u << 1);
	if (r > mid)update1(l, r, tar, mid + 1, right, u << 1 | 1);
	tree[u] = (tree[u << 1] + tree[u << 1 | 1]) % p;
}
void update2(ll l, ll r, ll tar, int left, int right, int u) {
	if (l <= left && r >= right) {
		tree[u] += tar * (right - left + 1);
		tree[u] %= p;
		add[u] += tar;
		add[u] %= p;
		return;
	}
	push_down(left,right,u);
	int mid = (left + right) >> 1;
	if (l <= mid)update2(l, r, tar, left, mid, u << 1);
	if (r > mid)update2(l, r, tar, mid + 1, right, u << 1 | 1);
	tree[u] = (tree[u << 1] + tree[u << 1 | 1]) % p;
}
ll query(ll l, ll r, int left, int right, int u) {
	if (l <= left && r >= right) {
		return tree[u];
	}
	push_down(left, right, u);
	int mid = (left + right) >> 1;
	ll sum = 0;
	if (l <= mid)sum =(sum+ query(l, r, left, mid, u << 1))%p;
	if (r > mid)sum =(sum+ query(l, r, mid + 1, right, u << 1 | 1))%p;
	return sum%p;
}
int main() {
	std::ios::sync_with_stdio(false);
	cin >> n >> p;
	for (int i = 1;i <= 4 * n;i++)mul[i] = 1;
	for (int i = 1;i <= n;i++) {
		cin >> num[i];
		num[i] %= p;
	}
	buildtree(1, n, 1);
	cin >> m;
	for (int i = 1;i <= m;i++) {
		int c;
		cin >> c;
		if (c == 1) {
			ll l, r, tar;
			cin >> l >> r >> tar;
			update1(l, r, tar, 1, n, 1);
		}
		if (c == 2) {
			ll l, r, tar;
			cin >> l >> r >> tar;
			update2(l, r, tar, 1, n, 1);
		}
		if (c == 3) {
			int l, r;
			cin >> l >> r;
			cout << query(l, r, 1, n, 1) << endl;
		}
	}
	return 0;
}

P1276 校门外的树(增强版)

在这里插入图片描述

#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e4 + 5;
ll tree[MAXN * 4];
ll mul[MAXN * 4];
ll add[MAXN * 4];
ll num[MAXN];
int n, m;
int ans1=0, ans2=0;
//染色类线段树,注意区间时从0到n,而不是一般的从1到n
void update_cut(int l, int r, int left, int right, int rt) {
	int mid = (left + right) / 2;
	if (tree[rt] != 1 && tree[rt] != -1) {//push_down
		tree[rt << 1] = tree[rt << 1 | 1] = tree[rt];
	}
	if (l <= left && r >= right) {
		if (tree[rt] == 0)return;//树为0表示子树都被砍了
		else if (tree[rt] == 1) {//为1就砍
			tree[rt] = 0;
		}
		else if (tree[rt] == 2) {//为2计算
			ans1 += (right - left + 1);
			ans2 = ans2 - (right - left + 1);
			tree[rt] = 0;
		}
		else if (tree[rt] == -1) {
			tree[rt] = 0;
			if(l<=mid)update_cut(l, r, left, mid, rt<<1);
			if (r > mid)update_cut(l, r, mid + 1, right, rt << 1 | 1);
		}
	}
	else {
		if (l <= mid)update_cut(l, r, left, mid, rt << 1);
		if (r > mid)update_cut(l, r, mid + 1, right, rt << 1 | 1);
		if (tree[rt << 1] == tree[rt << 1 | 1])tree[rt] = tree[rt << 1];
		else tree[rt] = -1;
	}
}
void update_plant(int l, int r, int left, int right, int rt) {
	if (tree[rt] != 1 && tree[rt] != -1) {
		tree[rt << 1] = tree[rt << 1 | 1] = tree[rt];
	}
	int mid = (left + right) >> 1;
	if (l <= left && r >= right) {
		if (tree[rt] == 1 || tree[rt] == 2)return;
		if (tree[rt] == 0) {
			tree[rt] = 2;
			ans2 += right - left + 1;
		}
		if (tree[rt] == -1) {//树也可能是1
			if (l <= mid)update_plant(l, r, left, mid, rt << 1);
			if (r > mid)update_plant(l, r, mid + 1, right, rt << 1 | 1);
			if (tree[rt << 1] == tree[rt << 1 | 1]) {
				tree[rt] = tree[rt << 1];
			}
			else tree[rt] = -1;
		}
	}
	else {
		if (l <= mid)update_plant(l, r, left, mid, rt << 1);
		if (r > mid)update_plant(l, r, mid + 1, right, rt << 1 | 1);
		if (tree[rt << 1] == tree[rt << 1 | 1]) {
			tree[rt] = tree[rt << 1];
		}
		else tree[rt] = -1;
	}
}
void swap(int l, int r) {
	int tmp = l;
	l = r;
	r = tmp;
}
int main() {
	std::ios::sync_with_stdio(false);
	cin >> n >> m;
	for (int i = 0;i <= 4 * n;i++)tree[i] = 1;
	for (int i = 1;i <= m;i++) {
		int c;
		cin >> c;
		if (c == 1) {
			int l, r;
			
			cin >> l >> r;
			if (l > r)swap(l, r);
			update_plant(l, r, 0, n, 1);
		}
		if (c == 0) {
			int l, r;
			cin >> l >> r;
			if (l > r)swap(l, r);
			update_cut(l, r, 0, n, 1);
		}

	}
	cout << ans2 << endl;
	cout << ans1 << endl;
	return 0;
}

P1531 I Hate It

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
ll tree[MAXN * 4];
ll mul[MAXN * 4];
ll add[MAXN * 4];
ll num[MAXN];
int p[MAXN];
int n, m;
//单点修改+区间查询模板
void buildtree(int left, int right, int rt) {
	if (left == right) {
		tree[rt] = num[left];
		p[left] = rt;
		return;
	}
	int mid = (left + right) >> 1;
	buildtree(left, mid, rt << 1);
	buildtree(mid + 1, right, rt << 1 | 1);
	tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);
}
void update(int pos, int tar, int left, int right, int rt) {
	if (left == right) {
		if (tree[rt] < tar) {
			tree[rt] = tar;
			
		}return;
	}
	int mid = (left + right) >> 1;
	if (pos <= mid)update(pos, tar, left, mid, rt << 1);
	else update(pos, tar, mid + 1, right, rt << 1 | 1);
	tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);
}
int query(int l, int r,int left,int right,int rt) {
	if (l <= left && r >= right) {
		return tree[rt];
	}
	int mid = (left + right) >> 1;
	int maxn = 0;
	if (l <= mid)maxn = max(maxn, query(l, r, left, mid, rt << 1));
	if (r > mid)maxn = max(maxn, query(l, r, mid + 1, right, rt << 1 | 1));
	return maxn;
}
int main() {
	cin >> n >> m;
	for (int i = 1;i <= n;i++) {
		cin >> num[i];
	}
	buildtree(1, n, 1);
	char ch;
	for (int i = 1;i <= m;i++) {
		cin >> ch;
		if (ch == 'Q') {
			int l, r;
			cin >> l >> r;
			cout << query(l, r, 1, n, 1) << endl;
		}
		if (ch == 'U') {
			int l, r;
			cin >> l >> r;
			update(l, r, 1, n, 1);
		}
	}
	return 0;
}

P5057 [CQOI2006]简单题

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
ll tree[MAXN * 4] = { 0 };
ll mul[MAXN * 4];
ll add[MAXN * 4];
ll num[MAXN];
int p[MAXN];
int n, m;
void push_down(int rt) {
	tree[rt << 1] ^= 1;
	tree[rt << 1 | 1] ^= 1;
	tree[rt] = 0;
	return;
}
void update(int l, int r, int left, int right, int rt) {
	if (l <= left && r >= right) {
		tree[rt] ^= 1;
		return;
	}
	if (tree[rt]) {
		push_down(rt);
	}
	int mid = (left + right) >> 1;
	if (l <= mid)update(l, r, left, mid, rt << 1);
	if (r > mid)update(l, r, mid + 1, right, rt << 1 | 1);
}
int query(int pos, int left, int right, int rt) {
	if (left == right)return tree[rt];
	if (tree[rt]) {
		push_down(rt);
	}
	int mid = (left + right) >> 1;
	if (pos <= mid)return query(pos, left, mid, rt << 1);
	else return query(pos, mid + 1, right, rt << 1 | 1);
}
int main() {
	cin >> n >> m;
	for (int i = 1;i <= m;i++) {
		int c;
		cin >> c;
		if (c == 1) {
			int l, r;
			cin >> l >> r;
			update(l, r, 1, n, 1);
		}
		if (c == 2) {
			int pos;
			cin >> pos;
			cout<<query(pos, 1, n, 1)<<endl;
		}
	}
	return 0;
}

P4588 [TJOI2018]数学计算

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
ll tree[MAXN * 4];
ll mul[MAXN * 4];
ll add[MAXN * 4];
ll num[MAXN];
int n, m;
int cnt = 0;
//可能是因为在线段树专题上,这题并没有那么难,但是这种题目很容易想到逆元
//对于线段树就是1和2的单点修改,1是乘上num[pos],2是化为1
void update(ll pos, int left, int right, int rt,int c) {
	if (left == right) {
		if (c == 1) {
			tree[rt] *= num[pos];
			tree[rt] %= m;
		}
		else {
			tree[rt] =1;
		}
		return;
	}
	int mid = (left + right) >> 1;
	if (pos <= mid)update(pos, left, mid, rt << 1,c);
	else update(pos, mid + 1, right, rt << 1 | 1,c);
	tree[rt] = (tree[rt << 1] * tree[rt << 1 | 1] % m);
}
int main() {
	std::ios::sync_with_stdio(false);
	int t;
	cin >> t;
	while (t--) {
		cin >> n >> m;
		memset(num, 0, sizeof num);
		memset(tree, 0, sizeof tree);
		cnt = 0;
		for (int i = 1;i <= n * 4;i++)tree[i] = 1;
		for (int i = 1;i <= n;i++) {
			int c;
			cin >> c;
			if (c == 1) {
				cin >> num[++cnt];
				update(cnt, 1, n, 1, 1);
				cout << tree[1] << endl;
			}
			if (c == 2) {
				cin >> num[++cnt];
				update(num[cnt], 1, n, 1, 2);
				cout << tree[1] << endl;
			}
		}
	}
	return 0;
}

P1908 逆序对

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e5 + 5;
ll tree[MAXN * 4];
ll mul[MAXN * 4];
ll add[MAXN * 4];
ll num[MAXN];
ll b[MAXN];
int n, m;
void update(ll pos, int left, int right, int rt) {
	if (left == right) {
		tree[rt] ++;
		return;
	}
	int mid = (left + right) >> 1;
	if (pos <= mid)update(pos, left, mid, rt << 1);
	else update(pos, mid + 1, right, rt << 1 | 1);
	tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
ll query(ll l, ll r, int left, int right, int rt) {
	if (l <= left && r >= right) {
		return tree[rt];
	}
	int mid = (left + right) >> 1;
	ll sum = 0;
	if (l <= mid)sum += query(l, r, left, mid, rt << 1);
	if (r > mid)sum += query(l, r, mid + 1, right, rt << 1 | 1);
	return sum;
}
int main() {
	std::ios::sync_with_stdio(false);
	cin >> n;
	ll ans = 0;
	for (int i = 1;i <= n;i++) { 
		cin >> num[i]; 
		b[i] = num[i];
	}
	//下面4行是离散化,因为数据为1e9,我们直接开线段树肯定不行,所以离散化
	sort(b + 1, b + 1 + n);//先对复制数组进行排序
	int sz = unique(b + 1, b + 1 + n) - b-1;//unique函数是对相邻相同的值伪去除,实际上是把相同的放到数组末尾,所以数组大小并没有改变,并且unique返回的是“去除”后的数组的尾位置,所以我们减去b-1可以得到不重复数组的长度
	for (int i = 1;i <= n;i++) {//对原数组进行离散化
		num[i] = lower_bound(b + 1, b + 1 + sz, num[i]) - b;//用lower_bound,在b数组中找到第一个小于等于num的数,为何减b,其实是减b+1,再+1,减去b+1得到第一个小于等于num数的位置,比如数组3 6 5 6 5 3 2 1,得到的值为2 4 3 4 3 2 1 0 所以我们+1,得到3 5 4 5 4 3 2 1
	}
	for (int i = n;i >= 1;i--) {
		update(num[i], 1, n, 1);
		if (num[i] == 1)continue;//1是最小的,防止下面num-1是的程序错误
		ans += query(1, num[i] - 1, 1, n, 1);
	}
	cout << ans << endl;
	return 0;
}

P1637 三元上升子序列

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e5 + 5;
ll tree[MAXN * 4];
ll mul[MAXN * 4];
ll add[MAXN * 4];
ll num[MAXN];
ll b[MAXN];
ll sm[MAXN];
ll bi[MAXN];
int n, m;
//2次逆序数,分别都是求一个数前面比他小的,和后面比他大的,因为数据为2的31次方,很大,所以我们离散化
void update(ll pos, int left, int right, int rt) {
	if (left == right) {
		tree[rt]++;
		return;
	}
	int mid = (left + right) >> 1;
	if (pos <= mid)update(pos, left, mid, rt << 1);
	else update(pos, mid + 1, right, rt << 1 | 1);
	tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
ll query(ll l, ll r, int left, int right, int rt) {
	if (l <= left && r >= right) {
		return tree[rt];
	}
	int mid = (left + right) >> 1;
	ll sum = 0;
	if (l <= mid)sum += query(l, r, left, mid, rt << 1);
	if (r > mid)sum += query(l, r, mid + 1, right, rt << 1 | 1);
	return sum;
}
int main() {
	std::ios::sync_with_stdio(false);
	cin >> n;
	for (int i = 1;i <= n;i++) {
		cin >> num[i];
		b[i] = num[i];
	}
	sort(b + 1, b + 1 + n);
	int sz = unique(b + 1, b + 1 + n) - b - 1;
	for (int i = 1;i <= n;i++) {
		num[i] = lower_bound(b + 1, b + 1 + sz, num[i]) - b;
	}
	for (int i = 1;i <= n;i++) {
		if (num[i] == 1) {
			update(num[i], 1, n, 1);
			continue;
		}
		sm[i] = query(1, num[i] - 1, 1, n, 1);
		update(num[i], 1, n, 1);
	}
	memset(tree, 0, sizeof tree);
	for (int i = n;i >= 1;i--) {
		if (num[i] == sz) {
			update(num[i], 1, n, 1);
			continue;
		}
		bi[i] = query(num[i] + 1, sz, 1, n, 1);
		update(num[i], 1, n, 1);
	}
	ll ans = 0;
	//cout << sz << endl;
	for (int i = 1;i <= n;i++) {
		//cout << i << " " <<num[i]<<" "<< sm[i] << " " << bi[i] << endl;
		ans += sm[i] * bi[i];
	}
	cout << ans << endl;
	return 0;
}

P6492 [COCI2010-2011#6] STEP

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
ll tree[MAXN * 4];
ll maxseg[MAXN * 4];
ll lmax[MAXN * 4], rmax[MAXN * 4];
bool L[MAXN * 4], R[MAXN * 4];
ll mul[MAXN * 4];
ll add[MAXN * 4];
ll num[MAXN];
ll len[MAXN * 4];
int n, m;
//这题也算经典类型的线段树,要维护多个区间值
void build(int left,int right,int rt) {//主要为了得到每个区间的长度
	len[rt] = right - left + 1; 
	if (left == right) { return; }
	int mid = (left + right) >> 1;
	build(left, mid, rt << 1);
	build(mid + 1, right, rt << 1 | 1);
}
void push_up(int left,int right,int rt) {
	if (R[rt << 1] ^ L[rt << 1 | 1]) {//如果左右子树的相接的点不同
		maxseg[rt] = rmax[rt << 1] + lmax[rt << 1 | 1];//这里必须是赋值,不能改为max(maxseg,rmax+lmax),因为有时候update后,maxseg会减少,如果max的话,就不会了
		maxseg[rt] = max(maxseg[rt], maxseg[rt << 1]);
		maxseg[rt] = max(maxseg[rt], maxseg[rt << 1 | 1]);
	}
	else maxseg[rt] = max(maxseg[rt << 1], maxseg[rt << 1 | 1]);
	if (lmax[rt << 1] ==len[rt<<1] && R[rt << 1] ^ L[rt << 1 | 1]) {//更改包含左结点的最大长度
		lmax[rt] = lmax[rt << 1] + lmax[rt << 1 | 1];
	}
	else {
		lmax[rt] = lmax[rt << 1];
	}
	if (rmax[rt << 1 | 1] == len[rt << 1 | 1] && R[rt << 1] ^ L[rt << 1 | 1]) {//包含右节点的最大长度
		rmax[rt] = rmax[rt << 1 | 1] + rmax[rt << 1];
	}
	else rmax[rt] = rmax[rt << 1 | 1];
	L[rt] = L[rt << 1];
	R[rt] = R[rt << 1 | 1];//更新左右结点
}
void update(int pos, int left, int right, int rt) {
	if (left == right) {
		L[rt] = R[rt] = !L[rt];
		return;
	}
	int mid = (left + right) >> 1;
	if (pos <= mid)update(pos, left, mid, rt << 1);
	else update(pos, mid + 1, right, rt << 1 | 1);
	push_up(left,right,rt);
	//cout << rt << " " << left << " " << right << " " << maxseg[rt] << endl;
}
int main() {
	std::ios::sync_with_stdio(false);
	cin >> n >> m;
	build(1, n, 1);
	for (int i = 1;i <= 4 * n;i++) {
		maxseg[i] = 1;
		L[i] = R[i] = 0;
		lmax[i] = rmax[i] = 1;
	}
	for (int i = 1;i <= m;i++) {
		int pos;
		cin >> pos;
		update(pos, 1, n, 1);
		cout << maxseg[1] << endl;
	}
	return 0;
}

P2894 [USACO08FEB]Hotel G

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
ll tree[MAXN * 4];
ll maxseg[MAXN * 4];
ll lmax[MAXN * 4], rmax[MAXN * 4];
bool L[MAXN * 4], R[MAXN * 4];
ll mul[MAXN * 4];
ll lazy[MAXN * 4];
ll num[MAXN];
ll len[MAXN * 4];
int n, m;
//这题我们要维护子树包含左端点最大值,包含右端点最大值,区间最大值
void buildtree(int left, int right, int rt) {
	len[rt]=maxseg[rt] = rmax[rt] = lmax[rt] = right - left + 1;
	if (left == right)return;
	int mid = (left + right) >> 1;
	buildtree(left, mid, rt << 1);
	buildtree(mid+1, right, rt << 1 | 1);
}
void push_up(int rt) {//这里push_up用来更新rt结点
	if (lmax[rt << 1] == len[rt << 1]) {//下面3步为正常的区间维护最值
		lmax[rt] = lmax[rt << 1] + lmax[rt << 1 | 1];
	}
	else lmax[rt] = lmax[rt << 1];
	if (rmax[rt << 1 | 1] == len[rt << 1 | 1]) {
		rmax[rt] = rmax[rt << 1 | 1] + rmax[rt << 1];
	}
	else rmax[rt] = rmax[rt << 1|1];
	maxseg[rt] = max(max(rmax[rt << 1] + lmax[rt << 1 | 1], maxseg[rt << 1]), maxseg[rt << 1 | 1]);
}
void push_down(int rt) {
	if (lazy[rt]) {
		if (lazy[rt] == 1) {
			maxseg[rt << 1] = maxseg[rt << 1 | 1] = 0;
			rmax[rt << 1] = lmax[rt << 1] = rmax[rt << 1 | 1] = lmax[rt << 1 | 1] = 0;
			lazy[rt << 1] = lazy[rt << 1 | 1] = 1;
		}
		else {
			maxseg[rt << 1] = rmax[rt << 1] = lmax[rt << 1] = len[rt<<1];
			maxseg[rt << 1 | 1] = lmax[rt << 1 | 1] = rmax[rt << 1 | 1] = len[rt << 1 | 1];
			lazy[rt << 1] = lazy[rt << 1 | 1] = 2;
		}
		lazy[rt] = 0;
	}
}
void update(int l, int r, int left, int right, int rt, int tag) {
	
	int mid = (left + right) >> 1;
	if (l <= left && r >= right) {//正常区间修改,只是这里判断tag
		if (tag == 1) {
			maxseg[rt] = lmax[rt] = rmax[rt] = 0;
			lazy[rt] = 1;
		}
		else {
			maxseg[rt] = lmax[rt] = rmax[rt] = len[rt];
			lazy[rt] = 2;
		}
		return;
	}
	push_down(rt);//如果不满足上面的if,我们就要进行拆开update,所以要push——down
	if (l <= mid)update(l, r, left, mid, rt << 1, tag);
	if (r > mid)update(l, r, mid + 1, right, rt << 1 | 1, tag);
	push_up(rt);//对于2个子树我们update后,我们要对root结点进行改变
}
int query(int tar, int left, int right, int rt) {
	push_down(rt);
	if (left == right)return left;
	int mid = (left + right) >> 1;
	if (maxseg[rt << 1] >= tar)return query(tar, left, mid, rt << 1);
	if (rmax[rt << 1] + lmax[rt << 1 | 1] >= tar)return mid - rmax[rt << 1] + 1;
	else return query(tar, mid + 1, right, rt << 1 | 1);
}
int main() {
	std::ios::sync_with_stdio(false);
	cin >> n >> m;
	buildtree(1, n, 1);
	for (int i = 1;i <= m;i++) {
		int c;
		cin >> c;
		if (c == 1) {
			int tar;
			cin >> tar;
			if (maxseg[1] >= tar) {
				int left = query(tar, 1, n, 1);
				cout << left << endl;
				update(left, left + tar-1, 1, n, 1, 1);
			}
			else cout << 0 << endl;

		}
		else {
			int left, leng;
			cin >> left >> leng;
			update(left, left + leng - 1, 1, n, 1, 2);
		}
		//cout << rmax[2] << " " << lmax[3] << endl;
		//cout << maxseg[3] << endl;
	}
	return 0;
}

P2184 贪婪大陆

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
ll tree[MAXN * 4];
ll maxseg[MAXN * 4];
ll lmax[MAXN * 4], rmax[MAXN * 4];
bool L[MAXN * 4], R[MAXN * 4];
ll mul[MAXN * 4];
ll lazy[MAXN * 4];
ll num[MAXN];
ll len[MAXN * 4];
int n, m;
bool visit[MAXN];
//这题属于叠加类的线段树,有点染色类线段树的意思,但是不一样,染色类表示自己对这个区间就直接实现覆盖,并且可能覆盖多种颜色
//但是这题并不是,这题是叠加,同样地方完全可以埋无数颗地雷
//那么这区间查询地雷怎么做,假设我们update(a,b),其中b为update的右端点,a为左端点,那么我们a到b区间就有1颗地雷,以外区间没有,那么我们query区间(x,y),我们对于1到x-1找update中右端点共有几个,对于y+1到n找左端点有几个(我们将这些总和,这些地雷保证埋不到区间x到y)。每次埋雷用cnt++,最后我们用cnt减去不埋在x到y的地雷,就是埋在x到y的地雷数
void update(int pos, int tag, int left, int right, int rt) {
	if (left == right) {
		if (tag == 1)lmax[rt]++;
		if (tag == 2)rmax[rt]++;
		return;
	}
	int mid=(left + right) >> 1;
	if (pos <= mid)update(pos, tag, left, mid, rt << 1);
	else update(pos, tag, mid + 1,right, rt << 1 | 1);
	if (tag == 1)lmax[rt] = lmax[rt << 1] + lmax[rt << 1 | 1];
	else rmax[rt] = rmax[rt << 1] + rmax[rt << 1 | 1];
}
int query(int l, int r, int tag,int left, int right, int rt) {
		if (l <= left && r >= right) {
			if (tag == 1)return lmax[rt];
			if (tag == 2)return rmax[rt];
		}
		int mid = (left + right) >> 1;
		int sum = 0;
		if (l <= mid)sum += query(l, r, tag, left, mid, rt << 1);
		if (r > mid)sum += query(l, r, tag, mid + 1, right, rt << 1 | 1);
		return sum;
	}
int main() {
	std::ios::sync_with_stdio(false);
	cin >> n >> m;
	int cnt = 0;
	for (int i = 1;i <= m;i++) {
		int c;
		cin >> c;
		if (c == 1) {
			cnt++;
			int l, r;
			cin >> l >> r;
			update(l, 1, 1, n, 1);
			update(r, 2, 1, n, 1);
		}
		if (c == 2) {
			int l, r;
			cin >> l >> r;
			int sum = 0;
			if (l >= 2)sum+=query(1, l - 1, 2, 1, n, 1);
			if (r <= n - 1)sum+=query(r + 1, n, 1, 1, n, 1);
			cout << cnt - sum << endl;
		}
	}
	return 0;
}

P1438 无聊的数列

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
ll tree[MAXN * 4];
ll maxseg[MAXN * 4];
ll lmax[MAXN * 4], rmax[MAXN * 4];
bool L[MAXN * 4], R[MAXN * 4];
ll mul[MAXN * 4];
ll lazy[MAXN * 4];
ll num[MAXN];
ll len[MAXN * 4];
int n, m;
bool visit[MAXN];
//一个区间加上等差数列,我们要想到差分,比如在l到r区间,我们用差分的话,在l到r区间加上等差数列,相当于在l加上K,在l+1到r加上D,在r+1处减去K+(l-r)*D
//我们用tree来维护差分值,num记录初始的数据
void push_down(int rt,int len) {
	if (lazy[rt]) {
		tree[rt << 1] += lazy[rt] * (len - len / 2);
		tree[rt << 1 | 1] += lazy[rt] * (len / 2);
		lazy[rt << 1] += lazy[rt];
		lazy[rt << 1 | 1] += lazy[rt];
		lazy[rt] = 0;
	}
}
void updatelr(int l, int r, int D, int left, int right, int rt) {
	if (l <= left && r >= right) {
		tree[rt] += D * (right - left + 1);
		lazy[rt] += D;
		return;
	}
	push_down(rt, right - left + 1);
	int mid = (left + right) >> 1;
	if (l <= mid)updatelr(l, r, D, left, mid, rt << 1);
	if (r > mid)updatelr(l, r, D, mid + 1, right, rt << 1 | 1);
	tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
int query(int l,int r, int left, int right, int rt) {
	if (l <= left && r >= right) {
		return tree[rt];
	}
	push_down(rt, right - left + 1);
	int mid = (left + right) >> 1;
	int sum = 0;
	if (l <= mid)sum += query(l, r, left, mid, rt << 1);
	if (r > mid)sum += query(l, r, mid + 1, right, rt << 1 | 1);
	return sum;
}
int main() {
	std::ios::sync_with_stdio(false);
	cin >> n >> m;
	for (int i = 1;i <= n;i++) {
		cin >> num[i];
	}
	for (int i = 1;i <= m;i++) {
		int c;
		cin >> c;
		if (c == 1) {
			int l, r, K, D;
			cin >> l >> r >> K >> D;
			updatelr(l,l, K, 1, n, 1);
			
			if (r > l) {
				updatelr(l + 1, r, D, 1, n, 1);//如果r==l的话我们我们这一步不用操作
			}
			if(r!=n)updatelr(r + 1,r+1, -(K+(r-l)*D), 1, n, 1);//如果r==n的话,就不用再r+1
		}
		if (c == 2) {
			int r;
			cin >> r;
			cout << num[r]+query(1, r, 1, n, 1) << endl;
		}
	}
	return 0;
}

P4145 上帝造题的七分钟2 / 花神游历各国

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
ll tree[MAXN * 4];
ll maxseg[MAXN * 4];
ll lmax[MAXN * 4], rmax[MAXN * 4];
bool L[MAXN * 4], R[MAXN * 4];
ll mul[MAXN * 4];
ll lazy[MAXN * 4];
ll num[MAXN];
ll len[MAXN * 4];
int n, m;
bool visit[MAXN];
//这题属于每个数的更新次数有限制,一个数最多更新6次,所以,假设最后把每个数都更新一遍需要6e5的复杂度,我们返回sum总和,维护sum,nlogm,为何是logm呢,我们暴力修改单点不应该是nm复杂度嘛,所以我们还要维护区间最大值,如果区间最大值>1,我们需要修改,其他我们不用,这样我们就省去多余步骤,复杂度降低。
void swap(int& l, int& r) {
	int tmp = l;
	l = r;
	r = tmp;
}//防止l和r顺序不同
void buildtree(int left, int right, int rt) {
	if (left == right) {
		tree[rt] = num[left];
		maxseg[rt] = num[left];
		return;
	}
	int mid = (left + right) >> 1;
	buildtree(left, mid, rt << 1);
	buildtree(mid + 1, right, rt << 1 | 1);
	tree[rt] = tree[rt << 1] + tree[rt << 1|1];
	maxseg[rt] = max(maxseg[rt << 1], maxseg[rt << 1 | 1]);
}
void update(int l, int r, int left, int right, int rt) {
	if (left == right) {
		maxseg[rt] = tree[rt] = sqrt(maxseg[rt]);//单点暴力修改
		return;
	}
	int mid = (left + right) >> 1;
	if (l <= mid && maxseg[rt << 1] > 1)update(l, r, left, mid, rt << 1);
	if (r > mid && maxseg[rt << 1 | 1] > 1)update(l, r, mid + 1, right, rt << 1 | 1);
	maxseg[rt] = max(maxseg[rt << 1], maxseg[rt << 1 | 1]);
	tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
ll query(int l, int r, int left, int right, int rt) {
	if (l <= left && r >= right) {
		return tree[rt];
	}
	int mid = (left + right) >> 1;
	ll sum = 0;
	if (l <= mid)sum += query(l, r, left, mid, rt << 1);
	if (r > mid)sum += query(l, r, mid + 1, right, rt << 1 | 1);
	return sum;
}
int main() {
	std::ios::sync_with_stdio(false);
	cin >> n;
	for (int i = 1;i <= n;i++)cin >> num[i];
	buildtree(1, n, 1);
	cin >> m;
	for (int i = 1;i <= m;i++) {
		int c;
		cin >> c;
		if (c == 0) {
			int l, r;
			cin >> l >> r;
			if (l > r)swap(l, r);
			update(l, r, 1, n, 1);
		}
		if (c == 1) {
			int l, r;
			cin >> l >> r;
			if (l > r)  swap(l, r); 
			cout << query(l, r, 1, n, 1) << endl;
		}
	}
	return 0;
}

P6327 区间加区间sin和

在这里插入图片描述


#include<iostream>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<functional>
#include<memory.h>
#include<stack>
#include<set>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
ll tree[MAXN << 2];
ll maxseg[MAXN * 4];
double cose[MAXN << 2], sine[MAXN << 2];
ll lazy[MAXN * 4];
ll num[MAXN];
ll len[MAXN * 4];
int n, m;
bool visit[MAXN];
//这题是卡常数的题目,所以建议用快读
int read() {
	char c = getchar();int ans = 0;
	while (c < '0' || c>'9') c = getchar();
	while (c >= '0' && c <= '9') ans = (ans << 1) + (ans << 3) + (c ^ 48), c = getchar();
	return ans;
}
//改变sin与cos,我们用到sin(a+x)=sina*cosx+cosa*sinx以及cos(a+x)=cosa*cosx-sina*sinx
void buildtree(int left, int right, int rt) {
	if (left == right) {
		sine[rt] = sin(num[left]);
		cose[rt] = cos(num[left]);
		return;
	}
	int mid = (left + right) >> 1;
	buildtree(left, mid, rt << 1);
	buildtree(mid + 1, right, rt << 1 | 1);
	cose[rt] = cose[rt << 1] + cose[rt << 1 | 1];//这边用了乘法分配律,将拆开的sin与cos乘法分配
	sine[rt] = sine[rt << 1] + sine[rt << 1 | 1];
}
void push_down(int rt) {
	if (lazy[rt]) {
		double s = sin(lazy[rt]), c = cos(lazy[rt]);
		double s1 = sine[rt << 1], s2 = sine[rt << 1 | 1], c1 = cose[rt << 1], c2 = cose[rt << 1 | 1];//自己弄懂公式,这个也会懂
		sine[rt << 1] = s1 * c + c1 * s;
		sine[rt << 1 | 1] = s2 * c + c2 * s;
		cose[rt << 1] = c1 * c - s1 * s;
		cose[rt << 1 | 1] = c2 * c - s2 * s;
		lazy[rt << 1] += lazy[rt];
		lazy[rt << 1 | 1] += lazy[rt];
		lazy[rt] = 0;
	}
}
void update(int l, int r, int tar, int left, int right, int rt) {
	if (l <= left && r >= right) {
		double c = cose[rt], s = sine[rt];
		sine[rt] = sin(tar) * c + cos(tar) * s;
		cose[rt] = cos(tar) * c - sin(tar) * s;
		lazy[rt] += tar;
		return;
	}
	push_down(rt);
	int mid = (left + right) >> 1;
	if (l <= mid)update(l, r, tar,left, mid, rt << 1);
	if (r > mid)update(l, r,tar, mid + 1, right, rt << 1 | 1);
	sine[rt] = sine[rt << 1] + sine[rt << 1 | 1];//乘法分配
	cose[rt] = cose[rt << 1] + cose[rt << 1 | 1];
}
double query(int l, int r, int left, int right, int rt) {
	if (l <= left && r >= right) {
		return sine[rt];
	}
	push_down(rt);
	int mid = (left + right) >> 1;
	double sum = 0;
	if (l <= mid)sum += query(l, r, left, mid, rt << 1);
	if (r > mid)sum += query(l, r, mid + 1, right, rt << 1 | 1);
	return sum;
}
int main() {
	
	n = read();
	for (int i = 1;i <= n;i++)num[i] = read();
	buildtree(1, n, 1);
	m = read();
	for (int i = 1;i <= m;i++) {
		int c;
		c = read();
		if (c == 1) {
			int l, r, tar;
			l = read(), r = read(), tar = read();
			update(l, r, tar, 1, n, 1);
		}
		if (c == 2) {
			int l, r;
			l = read(), r = read();
			cout << fixed << setprecision(1) << query(l, r, 1, n, 1) << endl;
		}
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值