1. 程式人生 > >線段樹相關練習

線段樹相關練習

任重而道遠

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long ll;
const int N = 1e5 + 5;
struct Node {
	ll val, maxn;
	Node *ls, *rs;
	void update (void) {
		val = ls -> val + rs -> val;
		maxn = max (ls -> maxn, rs -> maxn);
	}
}pool[N << 1], *tail = pool, *root, *zero;
ll n, m;
ll a[N];

ll read () {
	ll x = 0, jud = 1; char c = getchar ();
	while (c < '0' || c > '9') {if (c == '-') jud = -1; c = getchar ();}
	while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar ();}
	return x * jud;
}

void init () {
	zero = ++tail;
	zero -> ls = zero -> rs = zero;
	zero -> val = zero -> maxn = 0;
}

Node *newnode () {
	Node *nd = ++tail;
	nd -> ls = nd -> rs = zero;
	nd -> val = nd -> maxn = 0;
	return nd;
}

Node *build (ll l, ll r) {
	Node *nd = newnode ();
	if (l == r) {
		nd -> val = a[l];
		nd -> maxn = a[l];
	} else {
		int mid = l + r >> 1;
		nd -> ls = build (l, mid);
		nd -> rs = build (mid + 1, r);
		nd -> update ();
	}
	return nd;
}

void modify (Node *nd, ll l, ll r, ll o, ll val) {
	if (l == r) {
		nd -> val = val;
		nd -> maxn = val;
	} else {
		int mid = l + r >> 1;
		if (o <= mid) modify (nd -> ls, l, mid, o, val);
		else modify (nd -> rs, mid + 1, r, o, val);
		nd -> update ();
	}
}

void modify (Node *nd, ll l, ll r, ll L, ll R, ll Mod) {
	if (nd -> maxn < Mod) return;
	if (l == r) {
		nd -> val %= Mod;
		nd -> maxn = nd -> val;
	} else {
		int mid = l + r >> 1;
		if (L <= mid) modify (nd -> ls, l, mid, L, R, Mod);
		if (R > mid) modify (nd -> rs, mid + 1, r, L, R, Mod);
		nd -> update ();
	}
}

ll query (Node *nd, ll l, ll r, ll L, ll R) {
	if (L <= l && R >= r) return nd -> val;
	int mid = l + r >> 1;
	ll rt = 0;
	if (L <= mid) rt += query (nd -> ls, l, mid, L, R);
	if (mid < R) rt += query (nd -> rs, mid + 1, r, L, R);
	return rt;
}

int main () {
	freopen ("mod.in", "r", stdin);
	freopen ("mod.out", "w", stdout);
	n = read (), m = read (), init ();
	for (int i = 1; i <= n; i++) a[i] = read ();
	root = build (1, n);
	while (m--) {
		ll opt = read ();
		if (opt == 1) {
			ll l = read (), r = read ();
			printf ("%lld\n", query (root, 1, n, l, r));
		} else if (opt == 2) {
			ll l = read (), r = read (), x = read ();
			modify (root, 1, n, l, r, x);
		} else {
			ll k = read (), y = read ();
			modify (root, 1, n, k, y);
		}
	}
	return 0;
}

描述

給一個長為N的數列,有M次操作,每次操作時以下三種之一:

(1)修改數列中的一個數

(2)求數列中某連續一段所有數的兩兩乘積的和 mod 1000000007

(3)求數列中某連續一段所有相鄰兩數乘積的和 mod 1000000007

輸入

第一行兩個正整數N和M。
第二行N的整數表示這個數列。
接下來M行,每行開頭是一個字元,若該字元為'M',則表示一個修改操作,接下來兩個整數x和y,表示把x位置的值修改為y;若該字元為'Q',則表示一個詢問操作,接下來兩個整數x和y,表示對[x,y]區間做2號詢問;若該字元為'A',則表示一個詢問操作,接下來兩個整數x和y,表示對[x,y]區間做3號詢問。

輸出

對每一個詢問操作單獨輸出一行,表示答案。

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long ll;
const int N = 1e5 + 5, Mod = 1e9 + 7;
struct Node {
	ll val, sum, tot, lf, rg;
	Node *ls, *rs;
	
	void update (void) {
		sum = (ls -> sum + rs -> sum) % Mod;
		tot = (ls -> sum * rs -> sum % Mod + ls -> tot + rs -> tot) % Mod;
		lf = ls -> lf, rg = rs -> rg;
		val = ((ls -> val + rs -> val + ls -> rg * rs -> lf % Mod) % Mod + Mod) % Mod;
	}
}pool[N << 1], *tail = pool, *root, *zero;
struct Point {ll sum, tot;};
ll n, m, x, y;
ll a[N];

void init () {
	zero = ++tail;
	zero -> ls = zero -> rs = zero;
	zero -> val = zero -> sum = zero -> lf = zero -> rg = 0;
}

Node *newnode () {
	Node *nd = ++tail;
	nd -> ls = nd -> rs = zero;
	nd -> val = nd -> sum = nd -> lf = nd -> rg = 0;
	return nd;
}

Node *build (int l, int r) {
	Node *nd = newnode ();
	if (l == r) {
		nd -> val = 0;
		nd -> sum = nd -> lf = nd -> rg = (a[l] % Mod + Mod) % Mod;
	} else {
		int mid = l + r >> 1;
		nd -> ls = build (l, mid);
		nd -> rs = build (mid + 1, r);
		nd -> update ();
	}
	return nd;
}

void modify (Node *nd, int l, int r, int o, int val) {
	if (l == r) {
		nd -> sum = nd -> lf = nd -> rg = (val % Mod + Mod) % Mod;
	} else {
		int mid = l + r >> 1;
		if (o <= mid) modify (nd -> ls, l, mid, o, val);
		else modify (nd -> rs, mid + 1, r, o, val);
		nd -> update ();
	}
}

Point query_1 (Node *nd, int l, int r, int L, int R) {
	if (L <= l && R >= r) return (Point) {nd -> sum, nd -> tot};
	int mid = l + r >> 1, ok1 = 0, ok2 = 0;
	Point p1, p2;
	if (L <= mid) {
		p1 = query_1 (nd -> ls, l, mid, L, R);
		ok1 = 1;
	}
	if (R > mid) {
		p2 = query_1 (nd -> rs, mid + 1, r, L, R);
		ok2 = 1;
	}
	if (ok1 && ok2) return (Point) {((p1.sum + p2.sum) % Mod + Mod) % Mod, ((p1.sum * p2.sum % Mod + p1.tot + p2.tot) % Mod + Mod) % Mod};
	else if (ok1) return p1;
	else return p2;
}

ll query_2 (Node *nd, int l, int r, int L, int R) {
	if (L <= l && R >= r) return nd -> val;
	int mid = l + r >> 1, ok1 = 0, ok2 = 0;
	ll sww = 0;
	if (L <= mid) {
		sww = ((sww + query_2 (nd -> ls, l, mid, L, R)) % Mod + Mod) % Mod;
		ok1 = 1;
	}
	if (R > mid) {
		sww = ((sww + query_2 (nd -> rs, mid + 1, r, L, R)) % Mod + Mod) % Mod;
		ok2 = 1;
	}
	if (ok1 && ok2) sww = ((sww + nd -> ls -> rg * nd -> rs -> lf) % Mod + Mod) % Mod;
	return sww;
}

int main () {
	init ();
	scanf ("%lld%lld", &n, &m);
	for (int i = 1; i <= n; i++) scanf ("%lld", &a[i]);
	root = build (1, n);
	while (m--) {
		char opt = getchar();
		while (opt != 'Q' && opt != 'A' && opt != 'M') opt = getchar();
		scanf ("%lld%lld", &x, &y);
		if (opt == 'M') {
			modify (root, 1, n, x, y);
		} else if (opt == 'Q') {
			printf ("%lld\n", query_1 (root, 1, n, x, y).tot);
		} else {
			printf ("%lld\n", query_2 (root, 1, n, x, y));
		}
	}
	return 0;
}

描述

給一個長為N的數列,有M次操作,每次操作是以下兩種之一:

(1)修改數列中的一個數

(2)求數列中某位置在某次操作後的值

輸入

第一行兩個正整數N和M。
第二行N個整數表示這個數列。
接下來M行,每行開頭是一個字元,若該字元為'M',則表示一個修改操作,接下來兩個整數x和y,表示把x位置的值修改為y;若該字元為'Q',則表示一個詢問操作,接下來兩個整數x和y,表示求x位置在第y次操作後的值。

輸出

對每一個詢問操作單獨輸出一行,表示答案。

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 1e5 + 5;
struct Node {
	int val;
	Node *ls, *rs;
}pool[N * 20], *tail = pool, *root[N];
int n, m, x, y, a[N];
char opt[10];

Node *build (int l, int r) {
	Node *nd = ++tail;
	if (l == r) {
		nd -> val = a[l];
	} else {
		int mid = l + r >> 1;
		nd -> ls = build (l, mid);
		nd -> rs = build (mid + 1, r);
	}
	return nd;
}

Node *modify (Node *rt, int l, int r, int o, int val) {
	Node *nd = ++tail;
	if (l == r) {
		nd -> val = val;
	} else {
		int mid = l + r >> 1;
		if (o <= mid) {
			nd -> ls = modify (rt -> ls, l, mid, o, val);
			nd -> rs = rt -> rs;
		} else {
			nd -> ls = rt -> ls;
			nd -> rs = modify (rt -> rs, mid + 1, r, o, val);
		}
	}
	return nd;
}

int query (Node *nd, int l, int r, int o) {
	if (l == r) return nd -> val;
	int mid = l + r >> 1;
	if (o <= mid) return query (nd -> ls, l, mid, o);
	else return query (nd -> rs, mid + 1, r, o);
}

int main () {
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) scanf ("%d", &a[i]);
	root[0] = build (1, n);
	for (int i = 1; i <= m; i++) {
		scanf ("%s", opt);
		scanf ("%d%d", &x, &y);
		if (opt[0] == 'M') {
			root[i] = modify (root[i - 1], 1, n, x, y);
		} else {
			printf ("%d\n", query (root[y], 1, n, x));
			root[i] = root[i - 1];
		}
	}
	return 0;
}

描述

給一個空數列,有M次操作,每次操作是以下三種之一:

(1)在數列後加一個數

(2)求數列中某位置的值

(3)撤銷掉最後進行的若干次操作(1和3)

輸入

第一行一個正整數M。
接下來M行,每行開頭是一個字元,若該字元為'A',則表示一個加數操作,接下來一個整數x,表示在數列後加一個整數x;若該字元為'Q',則表示一個詢問操作,接下來一個整數x,表示求x位置的值;若該字元為'U',則表示一個撤銷操作,接下來一個整數x,表示撤銷掉最後進行的若干次操作。

輸出

對每一個詢問操作單獨輸出一行,表示答案。

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 1e5 + 5;
struct Node {
	int val;
	Node *ls, *rs;
}pool[N * 20], *tail = pool, *root[N], *zero;
int m, x, tot, siz[N];
char opt[10];

Node *newnode () {
	Node *nd = ++tail;
	nd -> ls = nd -> rs = zero;
	nd -> val = 0;
	return nd;
}

void init () {
	zero = ++tail;
	zero -> ls = zero -> rs = zero;
	zero -> val = 0;
	root[0] = newnode ();
}

Node *modify (Node *rt, int l, int r, int o, int val) {
	Node *nd = newnode ();
	if (l == r) {
		nd -> val = val;
	} else {
		int mid = l + r >> 1;
		if (o <= mid) {
			nd -> ls = modify (rt -> ls, l, mid, o, val);
			nd -> rs = rt -> rs;
		} else {
			nd -> ls = rt -> ls;
			nd -> rs = modify (rt -> rs, mid + 1, r, o, val);
		}
	}
	return nd;
}

int query (Node *nd, int l, int r, int o) {
	if (l == r) return nd -> val;
	int mid = l + r >> 1;
	if (o <= mid) return query (nd -> ls, l, mid, o);
	else return query (nd -> rs, mid + 1, r, o);
}

int main () {
	scanf ("%d", &m);
	init ();
	while (m--) {
		scanf ("%s", opt); scanf ("%d", &x);
		if (opt[0] == 'A') {
			tot++;
			siz[tot] = siz[tot - 1] + 1;
			root[tot] = modify (root[tot - 1], 1, N, siz[tot], x);
		} else if (opt[0] == 'Q') {
			printf ("%d\n", query (root[tot], 1, N, x));
		} else {
			tot++;
			siz[tot] = siz[tot - x - 1];
			root[tot] = root[tot - x - 1];
		}
	}
	return 0;
}

描述

給一個長為N的數列,有M次操作,每次操作是以下兩種之一:

(1)將某連續一段同時改成一個數

(2)求數列中某連續一段的和

輸入

第一行兩個正整數N和M。
第二行N的整數表示這個數列。
接下來M行,每行開頭是一個字元,若該字元為'M',則表示一個修改操作,接下來三個整數x、y和z,表示在[x,y]這段區間的數改為z;若該字元為'Q',則表示一個詢問操作,接下來兩個整數x和y,表示求[x,y]這段區間的和。

輸出

對每一個詢問操作單獨輸出一行,表示答案。

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long ll;
const int N = 1e5 + 5;
struct Node {
	ll val, tag, flag;
	Node *ls, *rs;
	void update (void) {
		val = ls -> val + rs -> val;
	}
}pool[N << 1], *tail = pool, *root, *zero;
ll n, m, a[N];

ll read () {
	ll x = 0, f = 1; char c = getchar ();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar ();}
	while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar ();}
	return x * f;
}

void init () {
	zero = ++tail;
	zero -> ls = zero -> rs = zero;
	zero -> val = zero -> tag = zero -> flag = 0;
}

Node *newnode () {
	Node *nd = ++tail;
	nd -> ls = nd -> rs = zero;
	nd -> val = nd -> tag = nd -> flag = 0;
	return nd;
}

void push_down (Node *nd, ll l, ll r) {
	if (nd -> flag) {
		int mid = l + r >> 1;
		nd -> ls -> val = nd -> tag * (mid - l + 1);
		nd -> rs -> val = nd -> tag * (r - mid);
		nd -> ls -> tag = nd -> rs -> tag = nd -> tag;
		nd -> ls -> flag = nd -> rs -> flag = 1;
		nd -> flag = nd -> tag = 0;
	}
}

Node *build (ll l, ll r) {
	Node *nd = newnode ();
	if (l == r) {
		nd -> val = a[l];
		nd -> tag = nd -> flag = 0;
	} else {
		int mid = l + r >> 1;
		nd -> ls = build (l, mid);
		nd -> rs = build (mid + 1, r);
		nd -> update ();
	}
	return nd;
}

void modify (Node *nd, ll l, ll r, ll L, ll R, ll val) {
	if (L <= l && R >= r) {
		nd -> val = val * (r - l + 1);
		nd -> tag = val, nd -> flag = 1;
		return ;
	}
	push_down (nd, l, r);
	int mid = l + r >> 1;
	if (L <= mid) modify (nd -> ls, l, mid, L, R, val);
	if (mid < R) modify (nd -> rs, mid + 1, r, L, R, val);
	nd -> update ();
	return ;
}

ll query (Node *nd, ll l, ll r, ll L, ll R) {
	if (L <= l && R >= r) return nd -> val;
	push_down (nd, l, r);
	int mid = l + r >> 1;
	ll sww = 0;
	if (L <= mid) sww += query (nd -> ls, l, mid, L, R);
	if (mid < R) sww += query (nd -> rs, mid + 1, r, L, R);
	return sww;
}

int main () {
	n = read (), m = read ();
	for (int i = 1; i <= n; i++) a[i] = read ();
	init (), root = build (1, n);
	while (m--) {
		char opt = getchar ();
		while (opt != 'M' && opt != 'Q') opt = getchar ();
		if (opt == 'M') {
			int x, y, z;
			x = read (), y = read (), z = read ();
			modify (root, 1, n, x, y, z);
		} else {
			int x, y;
			x = read (), y = read ();
			printf ("%lld\n", query (root, 1, n, x, y));
		}
	}
	return 0;
}

描述

給一個長為N的數列,有M次操作,每次操作是以下兩種之一:

(1)修改數列中的一個數

(2)求數列中有多少個數比它前面的數都大

輸入

第一行兩個正整數N和M。
第二行N的整數表示這個數列。
接下來M行,每行開頭是一個字元,若該字元為'M',則表示一個修改操作,接下來兩個整數x和y,表示把x位置的值修改為y;若該字元為'Q',則表示一個詢問操作,求數列中有多少個數比它前面的數都大。

輸出

對每一個詢問操作單獨輸出一行,表示答案。

這道題和樓房重建(bzoj2957)是一樣的

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 1e5 + 3;
struct Node {
	int val, sww;
	Node *ls, *rs;
}pool[N << 1], *tail = pool, *root;
int n, m, a[N];

int read () {
	int x = 0, f = 1; char c = getchar ();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar ();}
	while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar ();}
	return x * f;
}

int query (Node *nd, int l, int r, int val) {
	if (l == r) return nd -> val > val;
	int mid = l + r >> 1;
	if (nd -> ls -> val <= val) return query (nd -> rs, mid + 1, r, val);
	else return nd -> sww - nd -> ls -> sww + query (nd -> ls, l, mid, val);
}

void update (Node *nd, int l, int r) {
	nd -> val = max (nd -> ls -> val, nd -> rs -> val);
	int mid = l + r >> 1;
	nd -> sww = nd -> ls -> sww + query (nd -> rs, mid + 1, r, nd -> ls -> val);
}

Node *build (int l, int r) {
	Node *nd = ++tail;
	if (l == r) {
		nd -> val = a[l];
		nd -> sww = 1;
	} else {
		int mid = l + r >> 1;
		nd -> ls = build (l, mid);
		nd -> rs = build (mid + 1, r);
		update (nd, l, r);
	}
	return nd;
}

void modify (Node *nd, int l, int r, int o, int val) {
	if (l == r) {
		nd -> val = val;
		nd -> sww = 1;
		return ;
	} 
	int mid = l + r >> 1;
	if (o <= mid) modify (nd -> ls, l, mid, o, val);
	else modify (nd -> rs, mid + 1, r, o, val);
	update (nd, l, r);
	return ;
}

int main () {
	n = read (), m = read ();
	for (int i = 1; i <= n; i++) a[i] = read ();
	root = build (1, n);
	while (m--) {
		char opt = getchar ();
		while (opt != 'M' && opt != 'Q') opt = getchar ();
		if (opt == 'M') {
			int x = read (), y = read ();
			modify (root, 1, n, x, y);
		} else {
			printf ("%d\n", root -> sww);
		}
	}
	return 0;
}